diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d5f020294ed87..94f9be9c02c58 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -94,6 +94,7 @@ import { getExpandoInitializer, getHostSignatureFromJSDoc, getImmediatelyInvokedFunctionExpression, + getJSDocHost, getJSDocTypeTag, getLeftmostAccessExpression, getNameOfDeclaration, @@ -229,6 +230,7 @@ import { JSDocClassTag, JSDocEnumTag, JSDocFunctionType, + JSDocImportTag, JSDocOverloadTag, JSDocParameterTag, JSDocPropertyLikeTag, @@ -525,6 +527,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { var lastContainer: HasLocals; var delayedTypeAliases: (JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag)[]; var seenThisKeyword: boolean; + var jsDocImports: JSDocImportTag[]; // state used by control flow analysis var currentFlow: FlowNode; @@ -592,6 +595,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { file.symbolCount = symbolCount; file.classifiableNames = classifiableNames; delayedBindJSDocTypedefTag(); + bindJSDocImports(); } file = undefined!; @@ -603,6 +607,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { blockScopeContainer = undefined!; lastContainer = undefined!; delayedTypeAliases = undefined!; + jsDocImports = undefined!; seenThisKeyword = false; currentFlow = undefined!; currentBreakTarget = undefined; @@ -1181,6 +1186,9 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag); break; // In source files and blocks, bind functions first to match hoisting that occurs at runtime + case SyntaxKind.JSDocImportTag: + bindJSDocImportTag(node as JSDocImportTag); + break; case SyntaxKind.SourceFile: { bindEachFunctionsFirst((node as SourceFile).statements); bind((node as SourceFile).endOfFileToken); @@ -2065,6 +2073,14 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { } } + function bindJSDocImportTag(node: JSDocImportTag) { + bind(node.tagName); + + if (typeof node.comment !== "string") { + bindEach(node.comment); + } + } + function bindOptionalExpression(node: Expression, trueTarget: FlowLabel, falseTarget: FlowLabel) { doWithConditionalBranches(bind, node, trueTarget, falseTarget); if (!isOptionalChain(node) || isOutermostOptionalChain(node)) { @@ -2443,6 +2459,35 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { currentFlow = saveCurrentFlow; } + function bindJSDocImports() { + if (jsDocImports === undefined) { + return; + } + + const saveContainer = container; + const saveLastContainer = lastContainer; + const saveBlockScopeContainer = blockScopeContainer; + const saveParent = parent; + const saveCurrentFlow = currentFlow; + + for (const jsDocImportTag of jsDocImports) { + const host = getJSDocHost(jsDocImportTag); + const enclosingContainer = host ? getEnclosingContainer(host) as IsContainer | undefined : undefined; + const enclosingBlockScopeContainer = host ? getEnclosingBlockScopeContainer(host) as IsBlockScopedContainer | undefined : undefined; + container = enclosingContainer || file; + blockScopeContainer = enclosingBlockScopeContainer || file; + currentFlow = initFlowNode({ flags: FlowFlags.Start }); + parent = jsDocImportTag; + bind(jsDocImportTag.importClause); + } + + container = saveContainer; + lastContainer = saveLastContainer; + blockScopeContainer = saveBlockScopeContainer; + parent = saveParent; + currentFlow = saveCurrentFlow; + } + // The binder visits every node in the syntax tree so it is a convenient place to perform a single localized // check for reserved words used as identifiers in strict mode code, as well as `yield` or `await` in // [Yield] or [Await] contexts, respectively. @@ -2995,6 +3040,8 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { return (delayedTypeAliases || (delayedTypeAliases = [])).push(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag); case SyntaxKind.JSDocOverloadTag: return bind((node as JSDocOverloadTag).typeExpression); + case SyntaxKind.JSDocImportTag: + return (jsDocImports || (jsDocImports = [])).push(node as JSDocImportTag); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7cff9af11039d..daeb44b94c412 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11,8 +11,8 @@ import { AmbientModuleDeclaration, and, AnonymousType, + AnyImportOrJsDocImport, AnyImportOrReExport, - AnyImportSyntax, append, appendIfUnique, ArrayBindingPattern, @@ -581,6 +581,7 @@ import { isJSDocCallbackTag, isJSDocConstructSignature, isJSDocFunctionType, + isJSDocImportTag, isJSDocIndexSignature, isJSDocLinkLike, isJSDocMemberName, @@ -770,6 +771,7 @@ import { JSDocEnumTag, JSDocFunctionType, JSDocImplementsTag, + JSDocImportTag, JSDocLink, JSDocLinkCode, JSDocLinkPlain, @@ -3382,6 +3384,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.JSDocTypedefTag: case SyntaxKind.JSDocCallbackTag: case SyntaxKind.JSDocEnumTag: + case SyntaxKind.JSDocImportTag: // js type aliases do not resolve names from their host, so skip past it const root = getJSDocRoot(location); if (root) { @@ -3950,7 +3953,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { || (n === stopAt || isFunctionLike(n) && (!getImmediatelyInvokedFunctionExpression(n) || (getFunctionFlags(n) & FunctionFlags.AsyncGenerator)) ? "quit" : false)); } - function getAnyImportSyntax(node: Node): AnyImportSyntax | undefined { + function getAnyImportSyntax(node: Node): AnyImportOrJsDocImport | undefined { switch (node.kind) { case SyntaxKind.ImportEqualsDeclaration: return node as ImportEqualsDeclaration; @@ -4288,8 +4291,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration | VariableDeclaration, specifier: ImportOrExportSpecifier | BindingElement | PropertyAccessExpression, dontResolveAlias = false): Symbol | undefined { - const moduleSpecifier = getExternalModuleRequireArgument(node) || (node as ImportDeclaration | ExportDeclaration).moduleSpecifier!; + function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration | VariableDeclaration | JSDocImportTag, specifier: ImportOrExportSpecifier | BindingElement | PropertyAccessExpression, dontResolveAlias = false): Symbol | undefined { + const moduleSpecifier = getExternalModuleRequireArgument(node) || (node as ImportDeclaration | ExportDeclaration | JSDocImportTag).moduleSpecifier!; const moduleSymbol = resolveExternalModuleName(node, moduleSpecifier)!; // TODO: GH#18217 const name = !isPropertyAccessExpression(specifier) && specifier.propertyName || specifier.name; if (!isIdentifier(name)) { @@ -5014,6 +5017,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ? location : (isModuleDeclaration(location) ? location : location.parent && isModuleDeclaration(location.parent) && location.parent.name === location ? location.parent : undefined)?.name || (isLiteralImportTypeNode(location) ? location : undefined)?.argument.literal || + (isInJSFile(location) && isJSDocImportTag(location) ? location.moduleSpecifier : undefined) || (isVariableDeclaration(location) && location.initializer && isRequireCall(location.initializer, /*requireStringLiteralLikeArgument*/ true) ? location.initializer.arguments[0] : undefined) || findAncestor(location, isImportCall)?.arguments[0] || findAncestor(location, isImportDeclaration)?.moduleSpecifier || @@ -9800,12 +9804,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ImportClause: { const generatedSpecifier = getSpecifierForModuleSymbol(target.parent || target, context); // generate specifier (even though we're reusing and existing one) for ambient module reference include side effects const specifier = bundled ? factory.createStringLiteral(generatedSpecifier) : (node as ImportClause).parent.moduleSpecifier; + const attributes = isImportDeclaration(node.parent) ? node.parent.attributes : undefined; + const isTypeOnly = isJSDocImportTag((node as ImportClause).parent); addResult( factory.createImportDeclaration( /*modifiers*/ undefined, - factory.createImportClause(/*isTypeOnly*/ false, factory.createIdentifier(localName), /*namedBindings*/ undefined), + factory.createImportClause(isTypeOnly, factory.createIdentifier(localName), /*namedBindings*/ undefined), specifier, - (node as ImportClause).parent.attributes, + attributes, ), ModifierFlags.None, ); @@ -9814,10 +9820,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.NamespaceImport: { const generatedSpecifier = getSpecifierForModuleSymbol(target.parent || target, context); // generate specifier (even though we're reusing and existing one) for ambient module reference include side effects const specifier = bundled ? factory.createStringLiteral(generatedSpecifier) : (node as NamespaceImport).parent.parent.moduleSpecifier; + const isTypeOnly = isJSDocImportTag((node as NamespaceImport).parent.parent); addResult( factory.createImportDeclaration( /*modifiers*/ undefined, - factory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, factory.createNamespaceImport(factory.createIdentifier(localName))), + factory.createImportClause(isTypeOnly, /*name*/ undefined, factory.createNamespaceImport(factory.createIdentifier(localName))), specifier, (node as ImportClause).parent.attributes, ), @@ -9839,11 +9846,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ImportSpecifier: { const generatedSpecifier = getSpecifierForModuleSymbol(target.parent || target, context); // generate specifier (even though we're reusing and existing one) for ambient module reference include side effects const specifier = bundled ? factory.createStringLiteral(generatedSpecifier) : (node as ImportSpecifier).parent.parent.parent.moduleSpecifier; + const isTypeOnly = isJSDocImportTag((node as ImportSpecifier).parent.parent.parent); addResult( factory.createImportDeclaration( /*modifiers*/ undefined, factory.createImportClause( - /*isTypeOnly*/ false, + isTypeOnly, /*name*/ undefined, factory.createNamedImports([ factory.createImportSpecifier( @@ -42176,6 +42184,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function checkJSDocImportTag(node: JSDocImportTag) { + checkImportAttributes(node); + } + function checkJSDocImplementsTag(node: JSDocImplementsTag): void { const classLike = getEffectiveJSDocHost(node); if (!classLike || !isClassDeclaration(classLike) && !isClassExpression(classLike)) { @@ -46385,7 +46397,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function checkImportAttributes(declaration: ImportDeclaration | ExportDeclaration) { + function checkImportAttributes(declaration: ImportDeclaration | ExportDeclaration | JSDocImportTag) { const node = declaration.attributes; if (node) { const importAttributesType = getGlobalImportAttributesType(/*reportErrors*/ true); @@ -46412,7 +46424,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return grammarErrorOnNode(node, message); } - if (isImportDeclaration(declaration) ? declaration.importClause?.isTypeOnly : declaration.isTypeOnly) { + const isTypeOnly = isJSDocImportTag(declaration) || (isImportDeclaration(declaration) ? declaration.importClause?.isTypeOnly : declaration.isTypeOnly); + if (isTypeOnly) { return grammarErrorOnNode(node, isImportAttributes ? Diagnostics.Import_attributes_cannot_be_used_with_type_only_imports_or_exports : Diagnostics.Import_assertions_cannot_be_used_with_type_only_imports_or_exports); } @@ -46940,6 +46953,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkJSDocSatisfiesTag(node as JSDocSatisfiesTag); case SyntaxKind.JSDocThisTag: return checkJSDocThisTag(node as JSDocThisTag); + case SyntaxKind.JSDocImportTag: + return checkJSDocImportTag(node as JSDocImportTag); case SyntaxKind.IndexedAccessType: return checkIndexedAccessType(node as IndexedAccessTypeNode); case SyntaxKind.MappedType: @@ -47896,6 +47911,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if ( (isExternalModuleImportEqualsDeclaration(node.parent.parent) && getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node) || ((node.parent.kind === SyntaxKind.ImportDeclaration || node.parent.kind === SyntaxKind.ExportDeclaration) && (node.parent as ImportDeclaration).moduleSpecifier === node) || + (isInJSFile(node) && isJSDocImportTag(node.parent) && node.parent.moduleSpecifier === node) || ((isInJSFile(node) && isRequireCall(node.parent, /*requireStringLiteralLikeArgument*/ false)) || isImportCall(node.parent)) || (isLiteralTypeNode(node.parent) && isLiteralImportTypeNode(node.parent.parent) && node.parent.parent.argument === node.parent) ) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 15e1847f72ac3..01a525951f844 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -243,6 +243,7 @@ import { JSDocEnumTag, JSDocFunctionType, JSDocImplementsTag, + JSDocImportTag, JSDocNameReference, JSDocNonNullableType, JSDocNullableType, @@ -1826,6 +1827,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri return emitJSDocTypedefTag(node as JSDocTypedefTag); case SyntaxKind.JSDocSeeTag: return emitJSDocSeeTag(node as JSDocSeeTag); + case SyntaxKind.JSDocImportTag: + return emitJSDocImportTag(node as JSDocImportTag); // SyntaxKind.JSDocPropertyTag (see JSDocParameterTag, above) // Transformation nodes @@ -4006,6 +4009,25 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri emitJSDocComment(tag.comment); } + function emitJSDocImportTag(tag: JSDocImportTag) { + emitJSDocTagName(tag.tagName); + writeSpace(); + + if (tag.importClause) { + emit(tag.importClause); + writeSpace(); + + emitTokenWithComment(SyntaxKind.FromKeyword, tag.importClause.end, writeKeyword, tag); + writeSpace(); + } + + emitExpression(tag.moduleSpecifier); + if (tag.attributes) { + emitWithLeadingSpace(tag.attributes); + } + emitJSDocComment(tag.comment); + } + function emitJSDocNameReference(node: JSDocNameReference) { writeSpace(); writePunctuation("{"); diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 57594879a59ab..76e084c90d8f0 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -226,6 +226,7 @@ import { JSDocEnumTag, JSDocFunctionType, JSDocImplementsTag, + JSDocImportTag, JSDocLink, JSDocLinkCode, JSDocLinkPlain, @@ -857,6 +858,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode updateJSDocImplementsTag, createJSDocSeeTag, updateJSDocSeeTag, + createJSDocImportTag, + updateJSDocImportTag, createJSDocNameReference, updateJSDocNameReference, createJSDocMemberName, @@ -5523,6 +5526,26 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode : node; } + // @api + function createJSDocImportTag(tagName: Identifier | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes, comment?: string | NodeArray): JSDocImportTag { + const node = createBaseJSDocTag(SyntaxKind.JSDocImportTag, tagName ?? createIdentifier("import"), comment); + node.importClause = importClause; + node.moduleSpecifier = moduleSpecifier; + node.attributes = attributes; + node.comment = comment; + return node; + } + + function updateJSDocImportTag(node: JSDocImportTag, tagName: Identifier | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes: ImportAttributes | undefined, comment: string | NodeArray | undefined): JSDocImportTag { + return node.tagName !== tagName + || node.comment !== comment + || node.importClause !== importClause + || node.moduleSpecifier !== moduleSpecifier + || node.attributes !== attributes + ? update(createJSDocImportTag(tagName, importClause, moduleSpecifier, attributes, comment), node) + : node; + } + // @api function createJSDocText(text: string): JSDocText { const node = createBaseNode(SyntaxKind.JSDocText); @@ -7179,6 +7202,8 @@ function getDefaultTagNameForKind(kind: JSDocTag["kind"]): string { return "augments"; case SyntaxKind.JSDocImplementsTag: return "implements"; + case SyntaxKind.JSDocImportTag: + return "import"; default: return Debug.fail(`Unsupported kind: ${Debug.formatSyntaxKind(kind)}`); } diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index 249b442316cf3..012616ac5dc70 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -91,6 +91,7 @@ import { JSDocEnumTag, JSDocFunctionType, JSDocImplementsTag, + JSDocImportTag, JSDocLink, JSDocLinkCode, JSDocLinkPlain, @@ -1183,6 +1184,10 @@ export function isJSDocThrowsTag(node: Node): node is JSDocThrowsTag { return node.kind === SyntaxKind.JSDocThrowsTag; } +export function isJSDocImportTag(node: Node): node is JSDocImportTag { + return node.kind === SyntaxKind.JSDocImportTag; +} + // Synthesized list /** @internal */ diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 009e1cdf3ea8f..ca4bb9ed61275 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -175,6 +175,7 @@ import { JSDocEnumTag, JSDocFunctionType, JSDocImplementsTag, + JSDocImportTag, JSDocLink, JSDocLinkCode, JSDocLinkPlain, @@ -1125,6 +1126,7 @@ const forEachChildTable: ForEachChildTable = { [SyntaxKind.JSDocReadonlyTag]: forEachChildInJSDocTag, [SyntaxKind.JSDocDeprecatedTag]: forEachChildInJSDocTag, [SyntaxKind.JSDocOverrideTag]: forEachChildInJSDocTag, + [SyntaxKind.JSDocImportTag]: forEachChildInJSDocImportTag, [SyntaxKind.PartiallyEmittedExpression]: forEachChildInPartiallyEmittedExpression, }; @@ -1214,6 +1216,14 @@ function forEachChildInJSDocTag(node: JSDocUnknownTag | JSDocClassTag | JSDoc || (typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment)); } +function forEachChildInJSDocImportTag(node: JSDocImportTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { + return visitNode(cbNode, node.tagName) + || visitNode(cbNode, node.importClause) + || visitNode(cbNode, node.moduleSpecifier) + || visitNode(cbNode, node.attributes) + || (typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment)); +} + function forEachChildInPartiallyEmittedExpression(node: PartiallyEmittedExpression, cbNode: (node: Node) => T | undefined, _cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { return visitNode(cbNode, node.expression); } @@ -3875,7 +3885,7 @@ namespace Parser { } function parseJSDocType(): TypeNode { - scanner.setInJSDocType(true); + scanner.setSkipJsDocLeadingAsterisks(true); const pos = getNodePos(); if (parseOptional(SyntaxKind.ModuleKeyword)) { // TODO(rbuckton): We never set the type for a JSDocNamepathType. What should we put here? @@ -3893,13 +3903,13 @@ namespace Parser { } } - scanner.setInJSDocType(false); + scanner.setSkipJsDocLeadingAsterisks(false); return finishNode(moduleTag, pos); } const hasDotDotDot = parseOptional(SyntaxKind.DotDotDotToken); let type = parseTypeOrTypePredicate(); - scanner.setInJSDocType(false); + scanner.setSkipJsDocLeadingAsterisks(false); if (hasDotDotDot) { type = finishNode(factory.createJSDocVariadicType(type), pos); } @@ -8346,6 +8356,16 @@ namespace Parser { return parseImportEqualsDeclaration(pos, hasJSDoc, modifiers, identifier, isTypeOnly); } + const importClause = tryParseImportClause(identifier, afterImportPos, isTypeOnly); + const moduleSpecifier = parseModuleSpecifier(); + const attributes = tryParseImportAttributes(); + + parseSemicolon(); + const node = factory.createImportDeclaration(modifiers, importClause, moduleSpecifier, attributes); + return withJSDoc(finishNode(node, pos), hasJSDoc); + } + + function tryParseImportClause(identifier: Identifier | undefined, pos: number, isTypeOnly: boolean, skipJsDocLeadingAsterisks = false) { // ImportDeclaration: // import ImportClause from ModuleSpecifier ; // import ModuleSpecifier; @@ -8355,18 +8375,17 @@ namespace Parser { token() === SyntaxKind.AsteriskToken || // import * token() === SyntaxKind.OpenBraceToken // import { ) { - importClause = parseImportClause(identifier, afterImportPos, isTypeOnly); + importClause = parseImportClause(identifier, pos, isTypeOnly, skipJsDocLeadingAsterisks); parseExpected(SyntaxKind.FromKeyword); } - const moduleSpecifier = parseModuleSpecifier(); + return importClause; + } + + function tryParseImportAttributes() { const currentToken = token(); - let attributes: ImportAttributes | undefined; if ((currentToken === SyntaxKind.WithKeyword || currentToken === SyntaxKind.AssertKeyword) && !scanner.hasPrecedingLineBreak()) { - attributes = parseImportAttributes(currentToken); + return parseImportAttributes(currentToken); } - parseSemicolon(); - const node = factory.createImportDeclaration(modifiers, importClause, moduleSpecifier, attributes); - return withJSDoc(finishNode(node, pos), hasJSDoc); } function parseImportAttribute() { @@ -8422,7 +8441,7 @@ namespace Parser { return finished; } - function parseImportClause(identifier: Identifier | undefined, pos: number, isTypeOnly: boolean) { + function parseImportClause(identifier: Identifier | undefined, pos: number, isTypeOnly: boolean, skipJsDocLeadingAsterisks: boolean) { // ImportClause: // ImportedDefaultBinding // NameSpaceImport @@ -8437,7 +8456,9 @@ namespace Parser { !identifier || parseOptional(SyntaxKind.CommaToken) ) { + if (skipJsDocLeadingAsterisks) scanner.setSkipJsDocLeadingAsterisks(true); namedBindings = token() === SyntaxKind.AsteriskToken ? parseNamespaceImport() : parseNamedImportsOrExports(SyntaxKind.NamedImports); + if (skipJsDocLeadingAsterisks) scanner.setSkipJsDocLeadingAsterisks(false); } return finishNode(factory.createImportClause(isTypeOnly, identifier, namedBindings), pos); @@ -9073,6 +9094,9 @@ namespace Parser { case "throws": tag = parseThrowsTag(start, tagName, margin, indentText); break; + case "import": + tag = parseImportTag(start, tagName, margin, indentText); + break; default: tag = parseUnknownTag(start, tagName, margin, indentText); break; @@ -9445,13 +9469,29 @@ namespace Parser { return finishNode(factory.createJSDocSatisfiesTag(tagName, typeExpression, comments), start); } + function parseImportTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocImportTag { + const afterImportTagPos = scanner.getTokenFullStart(); + + let identifier: Identifier | undefined; + if (isIdentifier()) { + identifier = parseIdentifier(); + } + + const importClause = tryParseImportClause(identifier, afterImportTagPos, /*isTypeOnly*/ true, /*skipJsDocLeadingAsterisks*/ true); + const moduleSpecifier = parseModuleSpecifier(); + const attributes = tryParseImportAttributes(); + + const comments = margin !== undefined && indentText !== undefined ? parseTrailingTagComments(start, getNodePos(), margin, indentText) : undefined; + return finishNode(factory.createJSDocImportTag(tagName, importClause, moduleSpecifier, attributes, comments), start); + } + function parseExpressionWithTypeArgumentsForAugments(): ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression; } { const usedBrace = parseOptional(SyntaxKind.OpenBraceToken); const pos = getNodePos(); const expression = parsePropertyAccessEntityNameExpression(); - scanner.setInJSDocType(true); + scanner.setSkipJsDocLeadingAsterisks(true); const typeArguments = tryParseTypeArguments(); - scanner.setInJSDocType(false); + scanner.setSkipJsDocLeadingAsterisks(false); const node = factory.createExpressionWithTypeArguments(expression, typeArguments) as ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression; }; const res = finishNode(node, pos); if (usedBrace) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 80ba1d4a5a23d..0bce5b25a9a06 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -192,6 +192,7 @@ import { isImportTypeNode, isIncrementalCompilation, isInJSFile, + isJSDocImportTag, isLiteralImportTypeNode, isModifier, isModuleDeclaration, @@ -205,6 +206,7 @@ import { isStringLiteral, isStringLiteralLike, isTraceEnabled, + JSDocImportTag, JsonSourceFile, JsxEmit, length, @@ -868,7 +870,7 @@ export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: } /** @internal */ -export function isExclusivelyTypeOnlyImportOrExport(decl: ImportDeclaration | ExportDeclaration) { +export function isExclusivelyTypeOnlyImportOrExport(decl: ImportDeclaration | ExportDeclaration | JSDocImportTag) { if (isExportDeclaration(decl)) { return decl.isTypeOnly; } @@ -3346,7 +3348,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } if ((file.flags & NodeFlags.PossiblyContainsDynamicImport) || isJavaScriptFile) { - collectDynamicImportOrRequireCalls(file); + collectDynamicImportOrRequireOrJsDocImportCalls(file); } file.imports = imports || emptyArray; @@ -3403,7 +3405,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } } - function collectDynamicImportOrRequireCalls(file: SourceFile) { + function collectDynamicImportOrRequireOrJsDocImportCalls(file: SourceFile) { const r = /import|require/g; while (r.exec(file.text) !== null) { // eslint-disable-line no-null/no-null const node = getNodeAtPosition(file, r.lastIndex); @@ -3420,6 +3422,13 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here imports = append(imports, node.argument.literal); } + else if (isJavaScriptFile && isJSDocImportTag(node)) { + const moduleNameExpr = getExternalModuleName(node); + if (moduleNameExpr && isStringLiteral(moduleNameExpr) && moduleNameExpr.text) { + setParentRecursive(node, /*incremental*/ false); + imports = append(imports, moduleNameExpr); + } + } } } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 2c63db910328a..9a3331badd3f6 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -103,7 +103,7 @@ export interface Scanner { setTextPos(textPos: number): void; resetTokenState(pos: number): void; /** @internal */ - setInJSDocType(inType: boolean): void; + setSkipJsDocLeadingAsterisks(skip: boolean): void; // Invokes the provided callback then unconditionally restores the scanner to the state it // was in immediately prior to invoking the callback. The result of invoking the callback // is returned from this function. @@ -978,7 +978,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean var tokenFlags: TokenFlags; var commentDirectives: CommentDirective[] | undefined; - var inJSDocType = 0; + var skipJsDocLeadingAsterisks = 0; var scriptKind = ScriptKind.Unknown; var jsDocParsingMode = JSDocParsingMode.ParseAll; @@ -1032,7 +1032,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean setOnError, resetTokenState, setTextPos: resetTokenState, - setInJSDocType, + setSkipJsDocLeadingAsterisks, tryScan, lookAhead, scanRange, @@ -1892,7 +1892,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean return pos += 2, token = SyntaxKind.AsteriskAsteriskToken; } pos++; - if (inJSDocType && !asteriskSeen && (tokenFlags & TokenFlags.PrecedingLineBreak)) { + if (skipJsDocLeadingAsterisks && !asteriskSeen && (tokenFlags & TokenFlags.PrecedingLineBreak)) { // decoration at the start of a JSDoc comment line asteriskSeen = true; continue; @@ -2804,8 +2804,8 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean tokenFlags = TokenFlags.None; } - function setInJSDocType(inType: boolean) { - inJSDocType += inType ? 1 : -1; + function setSkipJsDocLeadingAsterisks(skip: boolean) { + skipJsDocLeadingAsterisks += skip ? 1 : -1; } } diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 80bcbfa3fc94c..8e813273f7d0c 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -110,6 +110,7 @@ import { isIndexSignatureDeclaration, isInterfaceDeclaration, isInternalDeclaration, + isJSDocImportTag, isJsonSourceFile, isLateVisibilityPaintedStatement, isLiteralImportTypeNode, @@ -1313,6 +1314,8 @@ export function transformDeclarations(context: TransformationContext) { } if (isDeclaration(input) && isDeclarationAndNotVisible(input)) return; + if (isJSDocImportTag(input)) return; + // Elide implementation signatures from overload sets if (isFunctionLike(input) && resolver.isImplementationOfOverload(input)) return; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ce6064fbdc409..fe828ad4a4529 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -439,6 +439,7 @@ export const enum SyntaxKind { JSDocPropertyTag, JSDocThrowsTag, JSDocSatisfiesTag, + JSDocImportTag, // Synthesized list SyntaxList, @@ -481,9 +482,9 @@ export const enum SyntaxKind { LastStatement = DebuggerStatement, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocSatisfiesTag, + LastJSDocNode = JSDocImportTag, FirstJSDocTagNode = JSDocTag, - LastJSDocTagNode = JSDocSatisfiesTag, + LastJSDocTagNode = JSDocImportTag, /** @internal */ FirstContextualKeyword = AbstractKeyword, /** @internal */ LastContextualKeyword = OfKeyword, } @@ -1036,7 +1037,8 @@ export type ForEachChildNodes = | JSDocThrowsTag | JSDocOverrideTag | JSDocSatisfiesTag - | JSDocOverloadTag; + | JSDocOverloadTag + | JSDocImportTag; /** @internal */ export type HasChildren = @@ -3646,7 +3648,7 @@ export type NamedExportBindings = // import d, { a, b as x } from "mod" => name = d, namedBinding: NamedImports = { elements: [{ name: a }, { name: x, propertyName: b}]} export interface ImportClause extends NamedDeclaration { readonly kind: SyntaxKind.ImportClause; - readonly parent: ImportDeclaration; + readonly parent: ImportDeclaration | JSDocImportTag; readonly isTypeOnly: boolean; readonly name?: Identifier; // Default binding readonly namedBindings?: NamedImportBindings; @@ -4058,6 +4060,14 @@ export interface JSDocSatisfiesExpression extends ParenthesizedExpression { readonly _jsDocSatisfiesExpressionBrand: never; } +export interface JSDocImportTag extends JSDocTag { + readonly kind: SyntaxKind.JSDocImportTag; + readonly parent: JSDoc; + readonly importClause?: ImportClause; + readonly moduleSpecifier: Expression; + readonly attributes?: ImportAttributes; +} + // NOTE: Ensure this is up-to-date with src/debug/debug.ts // dprint-ignore export const enum FlowFlags { @@ -5436,7 +5446,10 @@ export type TypePredicate = ThisTypePredicate | IdentifierTypePredicate | Assert export type AnyImportSyntax = ImportDeclaration | ImportEqualsDeclaration; /** @internal */ -export type AnyImportOrRequire = AnyImportSyntax | VariableDeclarationInitializedTo; +export type AnyImportOrJsDocImport = AnyImportSyntax | JSDocImportTag; + +/** @internal */ +export type AnyImportOrRequire = AnyImportOrJsDocImport | VariableDeclarationInitializedTo; /** @internal */ export type AnyImportOrBareOrAccessedRequire = AnyImportSyntax | VariableDeclarationInitializedTo; @@ -5471,7 +5484,7 @@ export interface ValidImportTypeNode extends ImportTypeNode { /** @internal */ export type AnyValidImportOrReExport = - | (ImportDeclaration | ExportDeclaration) & { moduleSpecifier: StringLiteral; } + | (ImportDeclaration | ExportDeclaration | JSDocImportTag) & { moduleSpecifier: StringLiteral; } | ImportEqualsDeclaration & { moduleReference: ExternalModuleReference & { expression: StringLiteral; }; } | RequireOrImportCall | ValidImportTypeNode; @@ -5496,7 +5509,7 @@ export interface RequireVariableDeclarationList extends VariableDeclarationList /** @internal */ export type LateVisibilityPaintedStatement = - | AnyImportSyntax + | AnyImportOrJsDocImport | VariableStatement | ClassDeclaration | FunctionDeclaration @@ -8767,6 +8780,8 @@ export interface NodeFactory { updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray | undefined): JSDocThrowsTag; createJSDocSatisfiesTag(tagName: Identifier | undefined, typeExpression: JSDocTypeExpression, comment?: string | NodeArray): JSDocSatisfiesTag; updateJSDocSatisfiesTag(node: JSDocSatisfiesTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression, comment: string | NodeArray | undefined): JSDocSatisfiesTag; + createJSDocImportTag(tagName: Identifier | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes, comment?: string | NodeArray): JSDocImportTag; + updateJSDocImportTag(node: JSDocImportTag, tagName: Identifier | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes: ImportAttributes | undefined, comment: string | NodeArray | undefined): JSDocImportTag; createJSDocText(text: string): JSDocText; updateJSDocText(node: JSDocText, text: string): JSDocText; createJSDocComment(comment?: string | NodeArray | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 184f68b0c6221..cf9c49fa53f83 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -354,6 +354,7 @@ import { JSDocArray, JSDocCallbackTag, JSDocEnumTag, + JSDocImportTag, JSDocMemberName, JSDocOverloadTag, JSDocParameterTag, @@ -3981,13 +3982,14 @@ export function isFunctionSymbol(symbol: Symbol | undefined) { } /** @internal */ -export function tryGetModuleSpecifierFromDeclaration(node: AnyImportOrBareOrAccessedRequire | AliasDeclarationNode | ExportDeclaration | ImportTypeNode): StringLiteralLike | undefined { +export function tryGetModuleSpecifierFromDeclaration(node: AnyImportOrBareOrAccessedRequire | AliasDeclarationNode | ExportDeclaration | ImportTypeNode | JSDocImportTag): StringLiteralLike | undefined { switch (node.kind) { case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: return findAncestor(node.initializer, (node): node is RequireOrImportCall => isRequireCall(node, /*requireStringLiteralLikeArgument*/ true))?.arguments[0]; case SyntaxKind.ImportDeclaration: case SyntaxKind.ExportDeclaration: + case SyntaxKind.JSDocImportTag: return tryCast(node.moduleSpecifier, isStringLiteralLike); case SyntaxKind.ImportEqualsDeclaration: return tryCast(tryCast(node.moduleReference, isExternalModuleReference)?.expression, isStringLiteralLike); @@ -4016,6 +4018,7 @@ export function tryGetImportFromModuleSpecifier(node: StringLiteralLike): AnyVal switch (node.parent.kind) { case SyntaxKind.ImportDeclaration: case SyntaxKind.ExportDeclaration: + case SyntaxKind.JSDocImportTag: return node.parent as AnyValidImportOrReExport; case SyntaxKind.ExternalModuleReference: return (node.parent as ExternalModuleReference).parent as AnyValidImportOrReExport; @@ -4030,10 +4033,11 @@ export function tryGetImportFromModuleSpecifier(node: StringLiteralLike): AnyVal } /** @internal */ -export function getExternalModuleName(node: AnyImportOrReExport | ImportTypeNode | ImportCall | ModuleDeclaration): Expression | undefined { +export function getExternalModuleName(node: AnyImportOrReExport | ImportTypeNode | ImportCall | ModuleDeclaration | JSDocImportTag): Expression | undefined { switch (node.kind) { case SyntaxKind.ImportDeclaration: case SyntaxKind.ExportDeclaration: + case SyntaxKind.JSDocImportTag: return node.moduleSpecifier; case SyntaxKind.ImportEqualsDeclaration: return node.moduleReference.kind === SyntaxKind.ExternalModuleReference ? node.moduleReference.expression : undefined; @@ -4063,8 +4067,8 @@ export function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqua } /** @internal */ -export function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean { - return node.kind === SyntaxKind.ImportDeclaration && !!node.importClause && !!node.importClause.name; +export function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration | JSDocImportTag): boolean { + return (node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.JSDocImportTag) && !!node.importClause && !!node.importClause.name; } /** @internal */ diff --git a/src/services/codefixes/convertToTypeOnlyImport.ts b/src/services/codefixes/convertToTypeOnlyImport.ts index 6b680e62ba817..29d4261c7578e 100644 --- a/src/services/codefixes/convertToTypeOnlyImport.ts +++ b/src/services/codefixes/convertToTypeOnlyImport.ts @@ -37,8 +37,8 @@ registerCodeFix({ const declaration = getDeclaration(context.sourceFile, context.span.start); if (declaration) { const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, declaration)); - const importDeclarationChanges = declaration.kind === SyntaxKind.ImportSpecifier && canConvertImportDeclarationForSpecifier(declaration, context.sourceFile, context.program) - ? textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, declaration.parent.parent.parent)) + const importDeclarationChanges = declaration.kind === SyntaxKind.ImportSpecifier && isImportDeclaration(declaration.parent.parent.parent) && canConvertImportDeclarationForSpecifier(declaration, context.sourceFile, context.program) + ? textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, declaration.parent.parent.parent as ImportDeclaration)) : undefined; const mainAction = createCodeFixAction( fixId, @@ -71,6 +71,7 @@ registerCodeFix({ } else if ( errorDeclaration?.kind === SyntaxKind.ImportSpecifier + && isImportDeclaration(errorDeclaration.parent.parent.parent) && !fixedImportDeclarations.has(errorDeclaration.parent.parent.parent) && canConvertImportDeclarationForSpecifier(errorDeclaration, diag.file, context.program) ) { diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 80400b140438e..78df7b3a90007 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -72,6 +72,7 @@ import { isImportEqualsDeclaration, isInJSFile, isIntrinsicJsxName, + isJSDocImportTag, isJsxClosingElement, isJsxOpeningFragment, isJsxOpeningLikeElement, @@ -694,6 +695,7 @@ function getNamespaceLikeImportText(declaration: AnyImportOrRequire) { return tryCast(declaration.name, isIdentifier)?.text; case SyntaxKind.ImportEqualsDeclaration: return declaration.name.text; + case SyntaxKind.JSDocImportTag: case SyntaxKind.ImportDeclaration: return tryCast(declaration.importClause?.namedBindings, isNamespaceImport)?.name.text; default: @@ -806,7 +808,7 @@ function createExistingImportMap(checker: TypeChecker, importingFile: SourceFile (importMap ||= createMultiMap()).add(getSymbolId(moduleSymbol), i.parent); } } - else if (i.kind === SyntaxKind.ImportDeclaration || i.kind === SyntaxKind.ImportEqualsDeclaration) { + else if (i.kind === SyntaxKind.ImportDeclaration || i.kind === SyntaxKind.ImportEqualsDeclaration || i.kind === SyntaxKind.JSDocImportTag) { const moduleSymbol = checker.getSymbolAtLocation(moduleSpecifier); if (moduleSymbol) { (importMap ||= createMultiMap()).add(getSymbolId(moduleSymbol), i); @@ -816,10 +818,16 @@ function createExistingImportMap(checker: TypeChecker, importingFile: SourceFile return { getImportsForExportInfo: ({ moduleSymbol, exportKind, targetFlags, symbol }: SymbolExportInfo): readonly FixAddToExistingImportInfo[] => { - // Can't use an es6 import for a type in JS. - if (!(targetFlags & SymbolFlags.Value) && isSourceFileJS(importingFile)) return emptyArray; const matchingDeclarations = importMap?.get(getSymbolId(moduleSymbol)); if (!matchingDeclarations) return emptyArray; + + // Can't use an es6 import for a type in JS. + if ( + isSourceFileJS(importingFile) + && !(targetFlags & SymbolFlags.Value) + && !every(matchingDeclarations, isJSDocImportTag) + ) return emptyArray; + const importKind = getImportKind(importingFile, exportKind, compilerOptions); return matchingDeclarations.map(declaration => ({ declaration, importKind, symbol, targetFlags })); }, diff --git a/src/services/codefixes/useDefaultImport.ts b/src/services/codefixes/useDefaultImport.ts index 92ef84495a7bb..11d647517593a 100644 --- a/src/services/codefixes/useDefaultImport.ts +++ b/src/services/codefixes/useDefaultImport.ts @@ -7,6 +7,7 @@ import { Identifier, isExternalModuleReference, isIdentifier, + isImportDeclaration, isImportEqualsDeclaration, isNamespaceImport, makeImport, @@ -51,7 +52,7 @@ function getInfo(sourceFile: SourceFile, pos: number): Info | undefined { if (isImportEqualsDeclaration(parent) && isExternalModuleReference(parent.moduleReference)) { return { importNode: parent, name, moduleSpecifier: parent.moduleReference.expression }; } - else if (isNamespaceImport(parent)) { + else if (isNamespaceImport(parent) && isImportDeclaration(parent.parent.parent)) { const importNode = parent.parent.parent; return { importNode, name, moduleSpecifier: importNode.moduleSpecifier }; } diff --git a/src/services/completions.ts b/src/services/completions.ts index 6ebdefce12490..197a2dbb3b36e 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -188,6 +188,7 @@ import { isJSDoc, isJSDocAugmentsTag, isJSDocImplementsTag, + isJSDocImportTag, isJSDocParameterTag, isJSDocTag, isJSDocTemplateTag, @@ -255,6 +256,7 @@ import { isVariableDeclaration, isVariableLike, JsDoc, + JSDocImportTag, JSDocParameterTag, JSDocPropertyTag, JSDocReturnTag, @@ -3202,6 +3204,7 @@ function getCompletionData( log("getCompletionData: Is inside comment: " + (timestamp() - start)); let insideJsDocTagTypeExpression = false; + let insideJsDocImportTag = false; let isInSnippetScope = false; if (insideComment) { if (hasDocComment(sourceFile, position)) { @@ -3242,25 +3245,30 @@ function getCompletionData( if (tag.tagName.pos <= position && position <= tag.tagName.end) { return { kind: CompletionDataKind.JsDocTagName }; } - const typeExpression = tryGetTypeExpressionFromTag(tag); - if (typeExpression) { - currentToken = getTokenAtPosition(sourceFile, position); - if ( - !currentToken || - (!isDeclarationName(currentToken) && - (currentToken.parent.kind !== SyntaxKind.JSDocPropertyTag || - (currentToken.parent as JSDocPropertyTag).name !== currentToken)) - ) { - // Use as type location if inside tag's type expression - insideJsDocTagTypeExpression = isCurrentlyEditingNode(typeExpression); - } + if (isJSDocImportTag(tag)) { + insideJsDocImportTag = true; } - if (!insideJsDocTagTypeExpression && isJSDocParameterTag(tag) && (nodeIsMissing(tag.name) || tag.name.pos <= position && position <= tag.name.end)) { - return { kind: CompletionDataKind.JsDocParameterName, tag }; + else { + const typeExpression = tryGetTypeExpressionFromTag(tag); + if (typeExpression) { + currentToken = getTokenAtPosition(sourceFile, position); + if ( + !currentToken || + (!isDeclarationName(currentToken) && + (currentToken.parent.kind !== SyntaxKind.JSDocPropertyTag || + (currentToken.parent as JSDocPropertyTag).name !== currentToken)) + ) { + // Use as type location if inside tag's type expression + insideJsDocTagTypeExpression = isCurrentlyEditingNode(typeExpression); + } + } + if (!insideJsDocTagTypeExpression && isJSDocParameterTag(tag) && (nodeIsMissing(tag.name) || tag.name.pos <= position && position <= tag.name.end)) { + return { kind: CompletionDataKind.JsDocParameterName, tag }; + } } } - if (!insideJsDocTagTypeExpression) { + if (!insideJsDocTagTypeExpression && !insideJsDocImportTag) { // Proceed if the current position is in jsDoc tag expression; otherwise it is a normal // comment or the plain text part of a jsDoc comment, so no completion should be available log("Returning an empty list because completion was inside a regular comment or plain text part of a JsDoc comment."); @@ -3271,7 +3279,7 @@ function getCompletionData( start = timestamp(); // The decision to provide completion depends on the contextToken, which is determined through the previousToken. // Note: 'previousToken' (and thus 'contextToken') can be undefined if we are the beginning of the file - const isJsOnlyLocation = !insideJsDocTagTypeExpression && isSourceFileJS(sourceFile); + const isJsOnlyLocation = !insideJsDocTagTypeExpression && !insideJsDocImportTag && isSourceFileJS(sourceFile); const tokens = getRelevantTokens(position, sourceFile); const previousToken = tokens.previousToken!; let contextToken = tokens.contextToken!; @@ -3952,6 +3960,7 @@ function getCompletionData( function isTypeOnlyCompletion(): boolean { return insideJsDocTagTypeExpression + || insideJsDocImportTag || !!importStatementCompletion && isTypeOnlyImportOrExportDeclaration(location.parent) || !isContextTokenValueLocation(contextToken) && (isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker) @@ -5742,9 +5751,9 @@ function getImportStatementCompletionInfo(contextToken: Node, sourceFile: Source } } -function getSingleLineReplacementSpanForImportCompletionNode(node: ImportDeclaration | ImportEqualsDeclaration | ImportSpecifier | Token | undefined) { +function getSingleLineReplacementSpanForImportCompletionNode(node: ImportDeclaration | ImportEqualsDeclaration | ImportSpecifier | JSDocImportTag | Token | undefined) { if (!node) return undefined; - const top = findAncestor(node, or(isImportDeclaration, isImportEqualsDeclaration)) ?? node; + const top = findAncestor(node, or(isImportDeclaration, isImportEqualsDeclaration, isJSDocImportTag)) ?? node; const sourceFile = top.getSourceFile(); if (rangeIsOnSingleLine(top, sourceFile)) { return createTextSpanFromNode(top, sourceFile); @@ -5753,7 +5762,7 @@ function getSingleLineReplacementSpanForImportCompletionNode(node: ImportDeclara Debug.assert(top.kind !== SyntaxKind.ImportKeyword && top.kind !== SyntaxKind.ImportSpecifier); // Guess which point in the import might actually be a later statement parsed as part of the import // during parser recovery - either in the middle of named imports, or the module specifier. - const potentialSplitPoint = top.kind === SyntaxKind.ImportDeclaration + const potentialSplitPoint = top.kind === SyntaxKind.ImportDeclaration || top.kind === SyntaxKind.JSDocImportTag ? getPotentiallyInvalidImportSpecifier(top.importClause?.namedBindings) ?? top.moduleSpecifier : top.moduleReference; const withoutModuleSpecifier: TextRange = { diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index b0e7215625e12..6f54bf158e48d 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -1,5 +1,6 @@ import { __String, + AnyImportOrJsDocImport, AnyImportOrReExport, AssignmentDeclarationKind, BinaryExpression, @@ -57,6 +58,7 @@ import { isVariableDeclaration, isVariableDeclarationInitializedToBareOrAccessedRequire, isVariableStatement, + JSDocImportTag, ModifierFlags, ModuleBlock, ModuleDeclaration, @@ -138,7 +140,7 @@ interface AmbientModuleDeclaration extends ModuleDeclaration { } type SourceFileLike = SourceFile | AmbientModuleDeclaration; // Identifier for the case of `const x = require("y")`. -type Importer = AnyImportOrReExport | ValidImportTypeNode | Identifier; +type Importer = AnyImportOrReExport | ValidImportTypeNode | Identifier | JSDocImportTag; type ImporterOrCallExpression = Importer | CallExpression; /** Returns import statements that directly reference the exporting module, and a list of files that may access the module through a namespace. */ @@ -215,6 +217,7 @@ function getImportersForExport( break; case SyntaxKind.ImportDeclaration: + case SyntaxKind.JSDocImportTag: directImports.push(direct); const namedBindings = direct.importClause && direct.importClause.namedBindings; if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) { @@ -267,7 +270,7 @@ function getImportersForExport( }); } - function handleNamespaceImport(importDeclaration: ImportEqualsDeclaration | ImportDeclaration, name: Identifier, isReExport: boolean, alreadyAddedDirect: boolean): void { + function handleNamespaceImport(importDeclaration: AnyImportOrJsDocImport, name: Identifier, isReExport: boolean, alreadyAddedDirect: boolean): void { if (exportKind === ExportKind.ExportEquals) { // This is a direct import, not import-as-namespace. if (!alreadyAddedDirect) directImports.push(importDeclaration); diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 6f387271048dc..a08937cfd7e99 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -131,6 +131,7 @@ const jsDocTagNames = [ "host", "ignore", "implements", + "import", "inheritdoc", "inner", "instance", diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index 29f3ab5482d08..14a794c526af5 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -40,6 +40,7 @@ import { isString, isStringLiteral, isStringLiteralLike, + JSDocImportTag, jsxModeNeedsExplicitImport, LanguageServiceHost, length, @@ -709,7 +710,7 @@ function detectModuleSpecifierCaseBySort(importDeclsByGroup: (readonly AnyImport return detectCaseSensitivityBySort(moduleSpecifiersByGroup, comparersToTest); } -function detectNamedImportOrganizationBySort(originalGroups: readonly ImportDeclaration[], comparersToTest: Comparer[], typesToTest: OrganizeImportsTypeOrder[]): { namedImportComparer: Comparer; typeOrder: OrganizeImportsTypeOrder | undefined; isSorted: boolean; } | undefined { +function detectNamedImportOrganizationBySort(originalGroups: readonly (ImportDeclaration | JSDocImportTag)[], comparersToTest: Comparer[], typesToTest: OrganizeImportsTypeOrder[]): { namedImportComparer: Comparer; typeOrder: OrganizeImportsTypeOrder | undefined; isSorted: boolean; } | undefined { // Filter for import declarations with named imports. Will be a flat array of import declarations without separations by group let bothNamedImports = false; const importDeclsWithNamed = originalGroups.filter(i => { @@ -884,7 +885,7 @@ function getNamedImportSpecifierComparer(pref } /** @internal */ -export function getNamedImportSpecifierComparerWithDetection(importDecl: ImportDeclaration, preferences: UserPreferences, sourceFile?: SourceFile): { specifierComparer: Comparer; isSorted: boolean | undefined; } { +export function getNamedImportSpecifierComparerWithDetection(importDecl: ImportDeclaration | JSDocImportTag, preferences: UserPreferences, sourceFile?: SourceFile): { specifierComparer: Comparer; isSorted: boolean | undefined; } { // sort case sensitivity: // - if the user preference is explicit, use that // - otherwise, if there are enough existing import specifiers in this import to detect unambiguously, use that diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index 5e6ac06ec846d..4dbfd40d89185 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -24,12 +24,14 @@ import { ImportSpecifier, isExportSpecifier, isImportDeclaration, + isJSDocImportTag, isPropertyAccessExpression, isPropertyAccessOrQualifiedName, isShorthandPropertyAssignment, isStringLiteral, NamedImports, NamespaceImport, + or, Program, PropertyAccessExpression, QualifiedName, @@ -110,8 +112,8 @@ function getImportConversionInfo(context: RefactorContext, considerPartialSpans const { file } = context; const span = getRefactorContextSpan(context); const token = getTokenAtPosition(file, span.start); - const importDecl = considerPartialSpans ? findAncestor(token, isImportDeclaration) : getParentNodeInSpan(token, file, span); - if (!importDecl || !isImportDeclaration(importDecl)) return { error: "Selection is not an import declaration." }; + const importDecl = considerPartialSpans ? findAncestor(token, or(isImportDeclaration, isJSDocImportTag)) : getParentNodeInSpan(token, file, span); + if (importDecl === undefined || !(isImportDeclaration(importDecl) || isJSDocImportTag(importDecl))) return { error: "Selection is not an import declaration." }; const end = span.start + span.length; const nextToken = findNextToken(importDecl, importDecl.parent, file); @@ -189,12 +191,13 @@ function doChangeNamespaceToNamed(sourceFile: SourceFile, checker: TypeChecker, }); const importDecl = toConvert.parent.parent; - if (usedAsNamespaceOrDefault && !allowSyntheticDefaultImports) { + if (usedAsNamespaceOrDefault && !allowSyntheticDefaultImports && isImportDeclaration(importDecl)) { // Need to leave the namespace import alone - changes.insertNodeAfter(sourceFile, importDecl, updateImport(importDecl, /*defaultImportName*/ undefined, importSpecifiers)); + changes.insertNodeAfter(sourceFile, importDecl, createImport(importDecl, /*defaultImportName*/ undefined, importSpecifiers)); } else { - changes.replaceNode(sourceFile, importDecl, updateImport(importDecl, usedAsNamespaceOrDefault ? factory.createIdentifier(toConvert.name.text) : undefined, importSpecifiers)); + const defaultImportName = usedAsNamespaceOrDefault ? factory.createIdentifier(toConvert.name.text) : undefined; + changes.replaceNode(sourceFile, toConvert.parent, createImportClause(defaultImportName, importSpecifiers)); } } @@ -266,9 +269,10 @@ export function doChangeNamedToNamespaceOrDefault(sourceFile: SourceFile, progra ? factory.createIdentifier(namespaceImportName) : factory.createNamespaceImport(factory.createIdentifier(namespaceImportName)), ); - if (neededNamedImports.size) { + + if (neededNamedImports.size && isImportDeclaration(importDecl)) { const newNamedImports: ImportSpecifier[] = arrayFrom(neededNamedImports.values(), element => factory.createImportSpecifier(element.isTypeOnly, element.propertyName && factory.createIdentifier(element.propertyName.text), factory.createIdentifier(element.name.text))); - changes.insertNodeAfter(sourceFile, toConvert.parent.parent, updateImport(importDecl, /*defaultImportName*/ undefined, newNamedImports)); + changes.insertNodeAfter(sourceFile, toConvert.parent.parent, createImport(importDecl, /*defaultImportName*/ undefined, newNamedImports)); } } @@ -279,6 +283,10 @@ function isExportEqualsModule(moduleSpecifier: Expression, checker: TypeChecker) return externalModule !== exportEquals; } -function updateImport(old: ImportDeclaration, defaultImportName: Identifier | undefined, elements: readonly ImportSpecifier[] | undefined): ImportDeclaration { - return factory.createImportDeclaration(/*modifiers*/ undefined, factory.createImportClause(/*isTypeOnly*/ false, defaultImportName, elements && elements.length ? factory.createNamedImports(elements) : undefined), old.moduleSpecifier, /*attributes*/ undefined); +function createImport(node: ImportDeclaration, defaultImportName: Identifier | undefined, elements: readonly ImportSpecifier[] | undefined): ImportDeclaration { + return factory.createImportDeclaration(/*modifiers*/ undefined, createImportClause(defaultImportName, elements), node.moduleSpecifier, /*attributes*/ undefined); +} + +function createImportClause(defaultImportName: Identifier | undefined, elements: readonly ImportSpecifier[] | undefined) { + return factory.createImportClause(/*isTypeOnly*/ false, defaultImportName, elements && elements.length ? factory.createNamedImports(elements) : undefined); } diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts index 5cb669be5368c..b5115345cd582 100644 --- a/src/services/stringCompletions.ts +++ b/src/services/stringCompletions.ts @@ -420,6 +420,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL case SyntaxKind.ImportDeclaration: case SyntaxKind.ExportDeclaration: case SyntaxKind.ExternalModuleReference: + case SyntaxKind.JSDocImportTag: // Get all known external module names or complete a path to a module // i.e. import * as ns from "/*completion position*/"; // var y = import("/*completion position*/"); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index cfc9c629837d4..ca9ebafe08bfc 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -188,6 +188,7 @@ import { isInternalModuleImportEqualsDeclaration, isJSDoc, isJSDocCommentContainingNode, + isJSDocImportTag, isJSDocLink, isJSDocLinkCode, isJSDocLinkLike, @@ -258,6 +259,7 @@ import { isWhiteSpaceSingleLine, isYieldExpression, IterationStatement, + JSDocImportTag, JSDocLink, JSDocLinkCode, JSDocLinkDisplayPart, @@ -1244,7 +1246,7 @@ function getAdjustedLocationForDeclaration(node: Node, forRename: boolean) { } } -function getAdjustedLocationForImportDeclaration(node: ImportDeclaration, forRename: boolean) { +function getAdjustedLocationForImportDeclaration(node: ImportDeclaration | JSDocImportTag, forRename: boolean) { if (node.importClause) { if (node.importClause.name && node.importClause.namedBindings) { // do not adjust if we have both a name and named bindings @@ -2578,6 +2580,7 @@ export function isModuleSpecifierLike(node: Node): node is StringLiteralLike { return isStringLiteralLike(node) && ( isExternalModuleReference(node.parent) || isImportDeclaration(node.parent) || + isJSDocImportTag(node.parent) || isRequireCall(node.parent, /*requireStringLiteralLikeArgument*/ false) && node.parent.arguments[0] === node || isImportCall(node.parent) && node.parent.arguments[0] === node ); diff --git a/src/testRunner/unittests/jsDocParsing.ts b/src/testRunner/unittests/jsDocParsing.ts index 56ba3a9cc05eb..0635aff6cdd1d 100644 --- a/src/testRunner/unittests/jsDocParsing.ts +++ b/src/testRunner/unittests/jsDocParsing.ts @@ -204,6 +204,34 @@ describe("unittests:: JSDocParsing", () => { */`, ); + parsesCorrectly( + "importTag1", + `/** + * @import foo from 'foo' + */`, + ); + + parsesCorrectly( + "importTag2", + `/** + * @import { foo } from 'foo' + */`, + ); + + parsesCorrectly( + "importTag3", + `/** + * @import * as types from 'foo' + */`, + ); + + parsesCorrectly( + "importTag4", + `/** + * @import * as types from 'foo' comment part + */`, + ); + parsesCorrectly( "returnTag1", `/** diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag1.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag1.json new file mode 100644 index 0000000000000..4b1926859ba6b --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag1.json @@ -0,0 +1,54 @@ +{ + "kind": "JSDoc", + "pos": 0, + "end": 35, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "tags": { + "0": { + "kind": "JSDocImportTag", + "pos": 8, + "end": 30, + "modifierFlagsCache": 0, + "transformFlags": 0, + "tagName": { + "kind": "Identifier", + "pos": 9, + "end": 15, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "import" + }, + "importClause": { + "kind": "ImportClause", + "pos": 16, + "end": 19, + "modifierFlagsCache": 0, + "transformFlags": 1, + "isTypeOnly": true, + "name": { + "kind": "Identifier", + "pos": 16, + "end": 19, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "foo" + } + }, + "moduleSpecifier": { + "kind": "StringLiteral", + "pos": 24, + "end": 30, + "modifierFlagsCache": 0, + "transformFlags": 0, + "text": "foo" + } + }, + "length": 1, + "pos": 8, + "end": 30, + "hasTrailingComma": false, + "transformFlags": 0 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag2.json new file mode 100644 index 0000000000000..a6f633d01dbec --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag2.json @@ -0,0 +1,76 @@ +{ + "kind": "JSDoc", + "pos": 0, + "end": 39, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "tags": { + "0": { + "kind": "JSDocImportTag", + "pos": 8, + "end": 34, + "modifierFlagsCache": 0, + "transformFlags": 0, + "tagName": { + "kind": "Identifier", + "pos": 9, + "end": 15, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "import" + }, + "importClause": { + "kind": "ImportClause", + "pos": 16, + "end": 23, + "modifierFlagsCache": 0, + "transformFlags": 1, + "isTypeOnly": true, + "namedBindings": { + "kind": "NamedImports", + "pos": 16, + "end": 23, + "modifierFlagsCache": 0, + "transformFlags": 0, + "elements": { + "0": { + "kind": "ImportSpecifier", + "pos": 17, + "end": 21, + "modifierFlagsCache": 0, + "transformFlags": 0, + "isTypeOnly": false, + "name": { + "kind": "Identifier", + "pos": 17, + "end": 21, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "foo" + } + }, + "length": 1, + "pos": 17, + "end": 21, + "hasTrailingComma": false, + "transformFlags": 0 + } + } + }, + "moduleSpecifier": { + "kind": "StringLiteral", + "pos": 28, + "end": 34, + "modifierFlagsCache": 0, + "transformFlags": 0, + "text": "foo" + } + }, + "length": 1, + "pos": 8, + "end": 34, + "hasTrailingComma": false, + "transformFlags": 0 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag3.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag3.json new file mode 100644 index 0000000000000..fd0b84c089d5a --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag3.json @@ -0,0 +1,61 @@ +{ + "kind": "JSDoc", + "pos": 0, + "end": 42, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "tags": { + "0": { + "kind": "JSDocImportTag", + "pos": 8, + "end": 37, + "modifierFlagsCache": 0, + "transformFlags": 0, + "tagName": { + "kind": "Identifier", + "pos": 9, + "end": 15, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "import" + }, + "importClause": { + "kind": "ImportClause", + "pos": 16, + "end": 26, + "modifierFlagsCache": 0, + "transformFlags": 1, + "isTypeOnly": true, + "namedBindings": { + "kind": "NamespaceImport", + "pos": 16, + "end": 26, + "modifierFlagsCache": 0, + "transformFlags": 0, + "name": { + "kind": "Identifier", + "pos": 20, + "end": 26, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "types" + } + } + }, + "moduleSpecifier": { + "kind": "StringLiteral", + "pos": 31, + "end": 37, + "modifierFlagsCache": 0, + "transformFlags": 0, + "text": "foo" + } + }, + "length": 1, + "pos": 8, + "end": 37, + "hasTrailingComma": false, + "transformFlags": 0 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag4.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag4.json new file mode 100644 index 0000000000000..f25a957cd57db --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.importTag4.json @@ -0,0 +1,62 @@ +{ + "kind": "JSDoc", + "pos": 0, + "end": 55, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "tags": { + "0": { + "kind": "JSDocImportTag", + "pos": 8, + "end": 53, + "modifierFlagsCache": 0, + "transformFlags": 0, + "tagName": { + "kind": "Identifier", + "pos": 9, + "end": 15, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "import" + }, + "comment": "comment part", + "importClause": { + "kind": "ImportClause", + "pos": 16, + "end": 26, + "modifierFlagsCache": 0, + "transformFlags": 1, + "isTypeOnly": true, + "namedBindings": { + "kind": "NamespaceImport", + "pos": 16, + "end": 26, + "modifierFlagsCache": 0, + "transformFlags": 0, + "name": { + "kind": "Identifier", + "pos": 20, + "end": 26, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "types" + } + } + }, + "moduleSpecifier": { + "kind": "StringLiteral", + "pos": 31, + "end": 37, + "modifierFlagsCache": 0, + "transformFlags": 0, + "text": "foo" + } + }, + "length": 1, + "pos": 8, + "end": 53, + "hasTrailingComma": false, + "transformFlags": 0 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 1601e677d29d5..3c7a59ea0da74 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3940,12 +3940,13 @@ declare namespace ts { JSDocPropertyTag = 348, JSDocThrowsTag = 349, JSDocSatisfiesTag = 350, - SyntaxList = 351, - NotEmittedStatement = 352, - PartiallyEmittedExpression = 353, - CommaListExpression = 354, - SyntheticReferenceExpression = 355, - Count = 356, + JSDocImportTag = 351, + SyntaxList = 352, + NotEmittedStatement = 353, + PartiallyEmittedExpression = 354, + CommaListExpression = 355, + SyntheticReferenceExpression = 356, + Count = 357, FirstAssignment = 64, LastAssignment = 79, FirstCompoundAssignment = 65, @@ -3974,9 +3975,9 @@ declare namespace ts { LastStatement = 259, FirstNode = 166, FirstJSDocNode = 309, - LastJSDocNode = 350, + LastJSDocNode = 351, FirstJSDocTagNode = 327, - LastJSDocTagNode = 350, + LastJSDocTagNode = 351, } type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -5426,7 +5427,7 @@ declare namespace ts { type NamedExportBindings = NamespaceExport | NamedExports; interface ImportClause extends NamedDeclaration { readonly kind: SyntaxKind.ImportClause; - readonly parent: ImportDeclaration; + readonly parent: ImportDeclaration | JSDocImportTag; readonly isTypeOnly: boolean; readonly name?: Identifier; readonly namedBindings?: NamedImportBindings; @@ -5783,6 +5784,13 @@ declare namespace ts { readonly kind: SyntaxKind.JSDocSatisfiesTag; readonly typeExpression: JSDocTypeExpression; } + interface JSDocImportTag extends JSDocTag { + readonly kind: SyntaxKind.JSDocImportTag; + readonly parent: JSDoc; + readonly importClause?: ImportClause; + readonly moduleSpecifier: Expression; + readonly attributes?: ImportAttributes; + } enum FlowFlags { Unreachable = 1, Start = 2, @@ -7696,6 +7704,8 @@ declare namespace ts { updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray | undefined): JSDocThrowsTag; createJSDocSatisfiesTag(tagName: Identifier | undefined, typeExpression: JSDocTypeExpression, comment?: string | NodeArray): JSDocSatisfiesTag; updateJSDocSatisfiesTag(node: JSDocSatisfiesTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression, comment: string | NodeArray | undefined): JSDocSatisfiesTag; + createJSDocImportTag(tagName: Identifier | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes, comment?: string | NodeArray): JSDocImportTag; + updateJSDocImportTag(node: JSDocImportTag, tagName: Identifier | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes: ImportAttributes | undefined, comment: string | NodeArray | undefined): JSDocImportTag; createJSDocText(text: string): JSDocText; updateJSDocText(node: JSDocText, text: string): JSDocText; createJSDocComment(comment?: string | NodeArray | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc; @@ -8998,6 +9008,7 @@ declare namespace ts { function isJSDocImplementsTag(node: Node): node is JSDocImplementsTag; function isJSDocSatisfiesTag(node: Node): node is JSDocSatisfiesTag; function isJSDocThrowsTag(node: Node): node is JSDocThrowsTag; + function isJSDocImportTag(node: Node): node is JSDocImportTag; function isQuestionOrExclamationToken(node: Node): node is QuestionToken | ExclamationToken; function isIdentifierOrThisTypeNode(node: Node): node is Identifier | ThisTypeNode; function isReadonlyKeywordOrPlusOrMinusToken(node: Node): node is ReadonlyKeyword | PlusToken | MinusToken; diff --git a/tests/baselines/reference/findAllRefsJsDocImportTag.baseline.jsonc b/tests/baselines/reference/findAllRefsJsDocImportTag.baseline.jsonc new file mode 100644 index 0000000000000..46b3bdd60105c --- /dev/null +++ b/tests/baselines/reference/findAllRefsJsDocImportTag.baseline.jsonc @@ -0,0 +1,105 @@ +// === findAllReferences === +// === /a.js === +// /** +// * <|@import { [|{| defId: 0, isWriteAccess: true |}A|] } from "./b"; +// |>*/ +// +// /** +// * @param { [|{| defId: 0 |}A|]/*FIND ALL REFS*/ } a +// */ +// function f(a) {} + +// === /b.ts === +// <|export interface [|{| defId: 1, isWriteAccess: true |}A|] { }|> + + // === Definitions === + // === /a.js === + // /** + // * <|@import { [|{| defId: 0 |}A|] } from "./b"; + // |>*/ + // + // /** + // * @param { A/*FIND ALL REFS*/ } a + // */ + // function f(a) {} + + // === /b.ts === + // <|export interface [|{| defId: 1 |}A|] { }|> + + // === Details === + [ + { + "defId": 0, + "containerKind": "", + "containerName": "", + "kind": "alias", + "name": "(alias) interface A\nimport A", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "alias", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "interface", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "A", + "kind": "aliasName" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": "import", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "A", + "kind": "aliasName" + } + ] + }, + { + "defId": 1, + "containerKind": "", + "containerName": "", + "kind": "interface", + "name": "interface A", + "displayParts": [ + { + "text": "interface", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "A", + "kind": "interfaceName" + } + ] + } + ] \ No newline at end of file diff --git a/tests/baselines/reference/goToDefinitionJsDocImportTag1.baseline.jsonc b/tests/baselines/reference/goToDefinitionJsDocImportTag1.baseline.jsonc new file mode 100644 index 0000000000000..2ac01a22845c7 --- /dev/null +++ b/tests/baselines/reference/goToDefinitionJsDocImportTag1.baseline.jsonc @@ -0,0 +1,17 @@ +// === goToDefinition === +// === /b.ts === +// [||]export interface A { } + +// === /a.js === +// /** +// * @import { A } from [|"./b/*GOTO DEF*/"|] +// */ + + // === Details === + [ + { + "kind": "script", + "name": "./b", + "unverified": false + } + ] \ No newline at end of file diff --git a/tests/baselines/reference/goToDefinitionJsDocImportTag2.baseline.jsonc b/tests/baselines/reference/goToDefinitionJsDocImportTag2.baseline.jsonc new file mode 100644 index 0000000000000..70db19f1b24fd --- /dev/null +++ b/tests/baselines/reference/goToDefinitionJsDocImportTag2.baseline.jsonc @@ -0,0 +1,5 @@ +// === goToDefinition === +// === /a.js === +// /** +// * @import { A } from/*GOTO DEF*/ "./b" +// */ \ No newline at end of file diff --git a/tests/baselines/reference/goToDefinitionJsDocImportTag3.baseline.jsonc b/tests/baselines/reference/goToDefinitionJsDocImportTag3.baseline.jsonc new file mode 100644 index 0000000000000..443b3ca3d9c62 --- /dev/null +++ b/tests/baselines/reference/goToDefinitionJsDocImportTag3.baseline.jsonc @@ -0,0 +1,5 @@ +// === goToDefinition === +// === /a.js === +// /** +// * @import { A } from /*GOTO DEF*/ "./b"; +// */ \ No newline at end of file diff --git a/tests/baselines/reference/goToDefinitionJsDocImportTag4.baseline.jsonc b/tests/baselines/reference/goToDefinitionJsDocImportTag4.baseline.jsonc new file mode 100644 index 0000000000000..8db114c5c52f3 --- /dev/null +++ b/tests/baselines/reference/goToDefinitionJsDocImportTag4.baseline.jsonc @@ -0,0 +1,20 @@ +// === goToDefinition === +// === /b.ts === +// <|export interface [|A|] { }|> + +// === /a.js === +// /** +// * @import { [|A|]/*GOTO DEF*/ } from "./b"; +// */ + + // === Details === + [ + { + "kind": "interface", + "name": "A", + "containerName": "\"/b\"", + "isLocal": false, + "isAmbient": false, + "unverified": false + } + ] \ No newline at end of file diff --git a/tests/baselines/reference/goToDefinitionJsDocImportTag5.baseline.jsonc b/tests/baselines/reference/goToDefinitionJsDocImportTag5.baseline.jsonc new file mode 100644 index 0000000000000..e58acd52951ca --- /dev/null +++ b/tests/baselines/reference/goToDefinitionJsDocImportTag5.baseline.jsonc @@ -0,0 +1,25 @@ +// === goToDefinition === +// === /b.ts === +// <|export interface [|A|] { }|> + +// === /a.js === +// /** +// * @import { A } from "./b"; +// */ +// +// /** +// * @param { [|A|]/*GOTO DEF*/ } a +// */ +// function f(a) {} + + // === Details === + [ + { + "kind": "interface", + "name": "A", + "containerName": "\"/b\"", + "isLocal": false, + "isAmbient": false, + "unverified": false + } + ] \ No newline at end of file diff --git a/tests/baselines/reference/importTag1.symbols b/tests/baselines/reference/importTag1.symbols new file mode 100644 index 0000000000000..ba55b1fac1cc9 --- /dev/null +++ b/tests/baselines/reference/importTag1.symbols @@ -0,0 +1,22 @@ +//// [tests/cases/conformance/jsdoc/importTag1.ts] //// + +=== /types.ts === +export interface Foo { +>Foo : Symbol(Foo, Decl(types.ts, 0, 0)) + + a: number; +>a : Symbol(Foo.a, Decl(types.ts, 0, 22)) +} + +=== /foo.js === +/** + * @import { Foo } from "./types" + */ + +/** + * @param { Foo } foo + */ +function f(foo) {} +>f : Symbol(f, Decl(foo.js, 0, 0)) +>foo : Symbol(foo, Decl(foo.js, 7, 11)) + diff --git a/tests/baselines/reference/importTag1.types b/tests/baselines/reference/importTag1.types new file mode 100644 index 0000000000000..b442a096969a8 --- /dev/null +++ b/tests/baselines/reference/importTag1.types @@ -0,0 +1,20 @@ +//// [tests/cases/conformance/jsdoc/importTag1.ts] //// + +=== /types.ts === +export interface Foo { + a: number; +>a : number +} + +=== /foo.js === +/** + * @import { Foo } from "./types" + */ + +/** + * @param { Foo } foo + */ +function f(foo) {} +>f : (foo: Foo) => void +>foo : Foo + diff --git a/tests/baselines/reference/importTag10.errors.txt b/tests/baselines/reference/importTag10.errors.txt new file mode 100644 index 0000000000000..496ce7f502595 --- /dev/null +++ b/tests/baselines/reference/importTag10.errors.txt @@ -0,0 +1,11 @@ +/foo.js(2,11): error TS1109: Expression expected. + + +==== /foo.js (1 errors) ==== + /** + * @import + + */ + +!!! error TS1109: Expression expected. + \ No newline at end of file diff --git a/tests/baselines/reference/importTag10.symbols b/tests/baselines/reference/importTag10.symbols new file mode 100644 index 0000000000000..f5b061a7abad6 --- /dev/null +++ b/tests/baselines/reference/importTag10.symbols @@ -0,0 +1,8 @@ +//// [tests/cases/conformance/jsdoc/importTag10.ts] //// + +=== /foo.js === + +/** + * @import + */ + diff --git a/tests/baselines/reference/importTag10.types b/tests/baselines/reference/importTag10.types new file mode 100644 index 0000000000000..f5b061a7abad6 --- /dev/null +++ b/tests/baselines/reference/importTag10.types @@ -0,0 +1,8 @@ +//// [tests/cases/conformance/jsdoc/importTag10.ts] //// + +=== /foo.js === + +/** + * @import + */ + diff --git a/tests/baselines/reference/importTag11.errors.txt b/tests/baselines/reference/importTag11.errors.txt new file mode 100644 index 0000000000000..e2c5c367f15d8 --- /dev/null +++ b/tests/baselines/reference/importTag11.errors.txt @@ -0,0 +1,13 @@ +/foo.js(2,15): error TS1109: Expression expected. +/foo.js(3,2): error TS1005: 'from' expected. + + +==== /foo.js (2 errors) ==== + /** + * @import foo + +!!! error TS1109: Expression expected. + */ + +!!! error TS1005: 'from' expected. + \ No newline at end of file diff --git a/tests/baselines/reference/importTag11.symbols b/tests/baselines/reference/importTag11.symbols new file mode 100644 index 0000000000000..4f1674c511c55 --- /dev/null +++ b/tests/baselines/reference/importTag11.symbols @@ -0,0 +1,8 @@ +//// [tests/cases/conformance/jsdoc/importTag11.ts] //// + +=== /foo.js === + +/** + * @import foo + */ + diff --git a/tests/baselines/reference/importTag11.types b/tests/baselines/reference/importTag11.types new file mode 100644 index 0000000000000..4f1674c511c55 --- /dev/null +++ b/tests/baselines/reference/importTag11.types @@ -0,0 +1,8 @@ +//// [tests/cases/conformance/jsdoc/importTag11.ts] //// + +=== /foo.js === + +/** + * @import foo + */ + diff --git a/tests/baselines/reference/importTag12.errors.txt b/tests/baselines/reference/importTag12.errors.txt new file mode 100644 index 0000000000000..f557cd1974d0b --- /dev/null +++ b/tests/baselines/reference/importTag12.errors.txt @@ -0,0 +1,10 @@ +/foo.js(2,20): error TS1109: Expression expected. + + +==== /foo.js (1 errors) ==== + /** + * @import foo from + +!!! error TS1109: Expression expected. + */ + \ No newline at end of file diff --git a/tests/baselines/reference/importTag12.symbols b/tests/baselines/reference/importTag12.symbols new file mode 100644 index 0000000000000..8c6cdc2734f7f --- /dev/null +++ b/tests/baselines/reference/importTag12.symbols @@ -0,0 +1,8 @@ +//// [tests/cases/conformance/jsdoc/importTag12.ts] //// + +=== /foo.js === + +/** + * @import foo from + */ + diff --git a/tests/baselines/reference/importTag12.types b/tests/baselines/reference/importTag12.types new file mode 100644 index 0000000000000..8c6cdc2734f7f --- /dev/null +++ b/tests/baselines/reference/importTag12.types @@ -0,0 +1,8 @@ +//// [tests/cases/conformance/jsdoc/importTag12.ts] //// + +=== /foo.js === + +/** + * @import foo from + */ + diff --git a/tests/baselines/reference/importTag13.errors.txt b/tests/baselines/reference/importTag13.errors.txt new file mode 100644 index 0000000000000..49025119c000b --- /dev/null +++ b/tests/baselines/reference/importTag13.errors.txt @@ -0,0 +1,13 @@ +/foo.js(1,15): error TS1005: 'from' expected. + + +==== /foo.js (1 errors) ==== + /** @import x = require("types") */ + ~ +!!! error TS1005: 'from' expected. + +==== /types.ts (0 errors) ==== + export interface Foo { + a: number; + } + \ No newline at end of file diff --git a/tests/baselines/reference/importTag13.symbols b/tests/baselines/reference/importTag13.symbols new file mode 100644 index 0000000000000..fbc59963b3da9 --- /dev/null +++ b/tests/baselines/reference/importTag13.symbols @@ -0,0 +1,6 @@ +//// [tests/cases/conformance/jsdoc/importTag13.ts] //// + +=== /foo.js === + +/** @import x = require("types") */ + diff --git a/tests/baselines/reference/importTag13.types b/tests/baselines/reference/importTag13.types new file mode 100644 index 0000000000000..fbc59963b3da9 --- /dev/null +++ b/tests/baselines/reference/importTag13.types @@ -0,0 +1,6 @@ +//// [tests/cases/conformance/jsdoc/importTag13.ts] //// + +=== /foo.js === + +/** @import x = require("types") */ + diff --git a/tests/baselines/reference/importTag14.errors.txt b/tests/baselines/reference/importTag14.errors.txt new file mode 100644 index 0000000000000..20360234a4bfb --- /dev/null +++ b/tests/baselines/reference/importTag14.errors.txt @@ -0,0 +1,14 @@ +/foo.js(1,33): error TS1464: Type import attributes should have exactly one key - 'resolution-mode' - with value 'import' or 'require'. +/foo.js(1,33): error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'. +/foo.js(1,38): error TS1005: '{' expected. + + +==== /foo.js (3 errors) ==== + /** @import * as f from "./foo" with */ + ~~~~ +!!! error TS1464: Type import attributes should have exactly one key - 'resolution-mode' - with value 'import' or 'require'. + ~~~~ +!!! error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'. + +!!! error TS1005: '{' expected. + \ No newline at end of file diff --git a/tests/baselines/reference/importTag14.symbols b/tests/baselines/reference/importTag14.symbols new file mode 100644 index 0000000000000..05e0be8659973 --- /dev/null +++ b/tests/baselines/reference/importTag14.symbols @@ -0,0 +1,6 @@ +//// [tests/cases/conformance/jsdoc/importTag14.ts] //// + +=== /foo.js === + +/** @import * as f from "./foo" with */ + diff --git a/tests/baselines/reference/importTag14.types b/tests/baselines/reference/importTag14.types new file mode 100644 index 0000000000000..05e0be8659973 --- /dev/null +++ b/tests/baselines/reference/importTag14.types @@ -0,0 +1,6 @@ +//// [tests/cases/conformance/jsdoc/importTag14.ts] //// + +=== /foo.js === + +/** @import * as f from "./foo" with */ + diff --git a/tests/baselines/reference/importTag15(module=es2015).errors.txt b/tests/baselines/reference/importTag15(module=es2015).errors.txt new file mode 100644 index 0000000000000..e3dba21b47669 --- /dev/null +++ b/tests/baselines/reference/importTag15(module=es2015).errors.txt @@ -0,0 +1,18 @@ +1.js(1,30): error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'. +1.js(2,33): error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'. + + +==== 0.ts (0 errors) ==== + export interface I { } + +==== 1.js (2 errors) ==== + /** @import { I } from './0' with { type: "json" } */ + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'. + /** @import * as foo from './0' with { type: "json" } */ + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'. + + /** @param {I} a */ + function f(a) {} + \ No newline at end of file diff --git a/tests/baselines/reference/importTag15(module=es2015).js b/tests/baselines/reference/importTag15(module=es2015).js new file mode 100644 index 0000000000000..ad2c1ac5a07d4 --- /dev/null +++ b/tests/baselines/reference/importTag15(module=es2015).js @@ -0,0 +1,25 @@ +//// [tests/cases/conformance/jsdoc/importTag15.ts] //// + +//// [0.ts] +export interface I { } + +//// [1.js] +/** @import { I } from './0' with { type: "json" } */ +/** @import * as foo from './0' with { type: "json" } */ + +/** @param {I} a */ +function f(a) {} + + + + +//// [0.d.ts] +export interface I { +} +//// [1.d.ts] +/** @import { I } from './0' with { type: "json" } */ +/** @import * as foo from './0' with { type: "json" } */ +/** @param {I} a */ +declare function f(a: I): void; +import type { I } from './0' with { type: "json" }; +import type * as foo from './0'; diff --git a/tests/baselines/reference/importTag15(module=es2015).symbols b/tests/baselines/reference/importTag15(module=es2015).symbols new file mode 100644 index 0000000000000..992eb71ede062 --- /dev/null +++ b/tests/baselines/reference/importTag15(module=es2015).symbols @@ -0,0 +1,15 @@ +//// [tests/cases/conformance/jsdoc/importTag15.ts] //// + +=== 0.ts === +export interface I { } +>I : Symbol(I, Decl(0.ts, 0, 0)) + +=== 1.js === +/** @import { I } from './0' with { type: "json" } */ +/** @import * as foo from './0' with { type: "json" } */ + +/** @param {I} a */ +function f(a) {} +>f : Symbol(f, Decl(1.js, 0, 0)) +>a : Symbol(a, Decl(1.js, 4, 11)) + diff --git a/tests/baselines/reference/importTag15(module=es2015).types b/tests/baselines/reference/importTag15(module=es2015).types new file mode 100644 index 0000000000000..fb18e6928b70b --- /dev/null +++ b/tests/baselines/reference/importTag15(module=es2015).types @@ -0,0 +1,15 @@ +//// [tests/cases/conformance/jsdoc/importTag15.ts] //// + +=== 0.ts === + +export interface I { } + +=== 1.js === +/** @import { I } from './0' with { type: "json" } */ +/** @import * as foo from './0' with { type: "json" } */ + +/** @param {I} a */ +function f(a) {} +>f : (a: I) => void +>a : I + diff --git a/tests/baselines/reference/importTag15(module=esnext).errors.txt b/tests/baselines/reference/importTag15(module=esnext).errors.txt new file mode 100644 index 0000000000000..3b98c15768bff --- /dev/null +++ b/tests/baselines/reference/importTag15(module=esnext).errors.txt @@ -0,0 +1,18 @@ +1.js(1,30): error TS2857: Import attributes cannot be used with type-only imports or exports. +1.js(2,33): error TS2857: Import attributes cannot be used with type-only imports or exports. + + +==== 0.ts (0 errors) ==== + export interface I { } + +==== 1.js (2 errors) ==== + /** @import { I } from './0' with { type: "json" } */ + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2857: Import attributes cannot be used with type-only imports or exports. + /** @import * as foo from './0' with { type: "json" } */ + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2857: Import attributes cannot be used with type-only imports or exports. + + /** @param {I} a */ + function f(a) {} + \ No newline at end of file diff --git a/tests/baselines/reference/importTag15(module=esnext).js b/tests/baselines/reference/importTag15(module=esnext).js new file mode 100644 index 0000000000000..ad2c1ac5a07d4 --- /dev/null +++ b/tests/baselines/reference/importTag15(module=esnext).js @@ -0,0 +1,25 @@ +//// [tests/cases/conformance/jsdoc/importTag15.ts] //// + +//// [0.ts] +export interface I { } + +//// [1.js] +/** @import { I } from './0' with { type: "json" } */ +/** @import * as foo from './0' with { type: "json" } */ + +/** @param {I} a */ +function f(a) {} + + + + +//// [0.d.ts] +export interface I { +} +//// [1.d.ts] +/** @import { I } from './0' with { type: "json" } */ +/** @import * as foo from './0' with { type: "json" } */ +/** @param {I} a */ +declare function f(a: I): void; +import type { I } from './0' with { type: "json" }; +import type * as foo from './0'; diff --git a/tests/baselines/reference/importTag15(module=esnext).symbols b/tests/baselines/reference/importTag15(module=esnext).symbols new file mode 100644 index 0000000000000..992eb71ede062 --- /dev/null +++ b/tests/baselines/reference/importTag15(module=esnext).symbols @@ -0,0 +1,15 @@ +//// [tests/cases/conformance/jsdoc/importTag15.ts] //// + +=== 0.ts === +export interface I { } +>I : Symbol(I, Decl(0.ts, 0, 0)) + +=== 1.js === +/** @import { I } from './0' with { type: "json" } */ +/** @import * as foo from './0' with { type: "json" } */ + +/** @param {I} a */ +function f(a) {} +>f : Symbol(f, Decl(1.js, 0, 0)) +>a : Symbol(a, Decl(1.js, 4, 11)) + diff --git a/tests/baselines/reference/importTag15(module=esnext).types b/tests/baselines/reference/importTag15(module=esnext).types new file mode 100644 index 0000000000000..fb18e6928b70b --- /dev/null +++ b/tests/baselines/reference/importTag15(module=esnext).types @@ -0,0 +1,15 @@ +//// [tests/cases/conformance/jsdoc/importTag15.ts] //// + +=== 0.ts === + +export interface I { } + +=== 1.js === +/** @import { I } from './0' with { type: "json" } */ +/** @import * as foo from './0' with { type: "json" } */ + +/** @param {I} a */ +function f(a) {} +>f : (a: I) => void +>a : I + diff --git a/tests/baselines/reference/importTag16.js b/tests/baselines/reference/importTag16.js new file mode 100644 index 0000000000000..63134d7d50256 --- /dev/null +++ b/tests/baselines/reference/importTag16.js @@ -0,0 +1,32 @@ +//// [tests/cases/conformance/jsdoc/importTag16.ts] //// + +//// [a.ts] +export default interface Foo {} +export interface I {} + +//// [b.js] +/** @import Foo, { I } from "./a" */ + +/** + * @param {Foo} a + * @param {I} b + */ +export function foo(a, b) {} + + + + +//// [a.d.ts] +export default interface Foo { +} +export interface I { +} +//// [b.d.ts] +/** @import Foo, { I } from "./a" */ +/** + * @param {Foo} a + * @param {I} b + */ +export function foo(a: Foo, b: I): void; +import type Foo from "./a"; +import type { I } from "./a"; diff --git a/tests/baselines/reference/importTag16.symbols b/tests/baselines/reference/importTag16.symbols new file mode 100644 index 0000000000000..d092f1c89e7bd --- /dev/null +++ b/tests/baselines/reference/importTag16.symbols @@ -0,0 +1,21 @@ +//// [tests/cases/conformance/jsdoc/importTag16.ts] //// + +=== a.ts === +export default interface Foo {} +>Foo : Symbol(Foo, Decl(a.ts, 0, 0)) + +export interface I {} +>I : Symbol(I, Decl(a.ts, 0, 31)) + +=== b.js === +/** @import Foo, { I } from "./a" */ + +/** + * @param {Foo} a + * @param {I} b + */ +export function foo(a, b) {} +>foo : Symbol(foo, Decl(b.js, 0, 0)) +>a : Symbol(a, Decl(b.js, 6, 20)) +>b : Symbol(b, Decl(b.js, 6, 22)) + diff --git a/tests/baselines/reference/importTag16.types b/tests/baselines/reference/importTag16.types new file mode 100644 index 0000000000000..13356e92a76d4 --- /dev/null +++ b/tests/baselines/reference/importTag16.types @@ -0,0 +1,19 @@ +//// [tests/cases/conformance/jsdoc/importTag16.ts] //// + +=== a.ts === + +export default interface Foo {} +export interface I {} + +=== b.js === +/** @import Foo, { I } from "./a" */ + +/** + * @param {Foo} a + * @param {I} b + */ +export function foo(a, b) {} +>foo : (a: Foo, b: I) => void +>a : Foo +>b : I + diff --git a/tests/baselines/reference/importTag2.symbols b/tests/baselines/reference/importTag2.symbols new file mode 100644 index 0000000000000..549bf3dd77536 --- /dev/null +++ b/tests/baselines/reference/importTag2.symbols @@ -0,0 +1,22 @@ +//// [tests/cases/conformance/jsdoc/importTag2.ts] //// + +=== /types.ts === +export interface Foo { +>Foo : Symbol(Foo, Decl(types.ts, 0, 0)) + + a: number; +>a : Symbol(Foo.a, Decl(types.ts, 0, 22)) +} + +=== /foo.js === +/** + * @import * as types from "./types" + */ + +/** + * @param { types.Foo } foo + */ +export function f(foo) {} +>f : Symbol(f, Decl(foo.js, 0, 0)) +>foo : Symbol(foo, Decl(foo.js, 7, 18)) + diff --git a/tests/baselines/reference/importTag2.types b/tests/baselines/reference/importTag2.types new file mode 100644 index 0000000000000..5a18d5e849e91 --- /dev/null +++ b/tests/baselines/reference/importTag2.types @@ -0,0 +1,20 @@ +//// [tests/cases/conformance/jsdoc/importTag2.ts] //// + +=== /types.ts === +export interface Foo { + a: number; +>a : number +} + +=== /foo.js === +/** + * @import * as types from "./types" + */ + +/** + * @param { types.Foo } foo + */ +export function f(foo) {} +>f : (foo: types.Foo) => void +>foo : types.Foo + diff --git a/tests/baselines/reference/importTag3.symbols b/tests/baselines/reference/importTag3.symbols new file mode 100644 index 0000000000000..9c6ecab2ed79f --- /dev/null +++ b/tests/baselines/reference/importTag3.symbols @@ -0,0 +1,22 @@ +//// [tests/cases/conformance/jsdoc/importTag3.ts] //// + +=== /types.ts === +export default interface Foo { +>Foo : Symbol(Foo, Decl(types.ts, 0, 0)) + + a: number; +>a : Symbol(Foo.a, Decl(types.ts, 0, 30)) +} + +=== /foo.js === +/** + * @import Foo from "./types" + */ + +/** + * @param { Foo } foo + */ +export function f(foo) {} +>f : Symbol(f, Decl(foo.js, 0, 0)) +>foo : Symbol(foo, Decl(foo.js, 7, 18)) + diff --git a/tests/baselines/reference/importTag3.types b/tests/baselines/reference/importTag3.types new file mode 100644 index 0000000000000..da6d62466da82 --- /dev/null +++ b/tests/baselines/reference/importTag3.types @@ -0,0 +1,20 @@ +//// [tests/cases/conformance/jsdoc/importTag3.ts] //// + +=== /types.ts === +export default interface Foo { + a: number; +>a : number +} + +=== /foo.js === +/** + * @import Foo from "./types" + */ + +/** + * @param { Foo } foo + */ +export function f(foo) {} +>f : (foo: Foo) => void +>foo : Foo + diff --git a/tests/baselines/reference/importTag4.errors.txt b/tests/baselines/reference/importTag4.errors.txt new file mode 100644 index 0000000000000..dbfd8ad5b4132 --- /dev/null +++ b/tests/baselines/reference/importTag4.errors.txt @@ -0,0 +1,27 @@ +/foo.js(2,14): error TS2300: Duplicate identifier 'Foo'. +/foo.js(6,14): error TS2300: Duplicate identifier 'Foo'. + + +==== /types.ts (0 errors) ==== + export interface Foo { + a: number; + } + +==== /foo.js (2 errors) ==== + /** + * @import { Foo } from "./types" + ~~~ +!!! error TS2300: Duplicate identifier 'Foo'. + */ + + /** + * @import { Foo } from "./types" + ~~~ +!!! error TS2300: Duplicate identifier 'Foo'. + */ + + /** + * @param { Foo } foo + */ + function f(foo) {} + \ No newline at end of file diff --git a/tests/baselines/reference/importTag4.symbols b/tests/baselines/reference/importTag4.symbols new file mode 100644 index 0000000000000..67ce4c4fc307d --- /dev/null +++ b/tests/baselines/reference/importTag4.symbols @@ -0,0 +1,26 @@ +//// [tests/cases/conformance/jsdoc/importTag4.ts] //// + +=== /types.ts === +export interface Foo { +>Foo : Symbol(Foo, Decl(types.ts, 0, 0)) + + a: number; +>a : Symbol(Foo.a, Decl(types.ts, 0, 22)) +} + +=== /foo.js === +/** + * @import { Foo } from "./types" + */ + +/** + * @import { Foo } from "./types" + */ + +/** + * @param { Foo } foo + */ +function f(foo) {} +>f : Symbol(f, Decl(foo.js, 0, 0)) +>foo : Symbol(foo, Decl(foo.js, 11, 11)) + diff --git a/tests/baselines/reference/importTag4.types b/tests/baselines/reference/importTag4.types new file mode 100644 index 0000000000000..473a932a89ce2 --- /dev/null +++ b/tests/baselines/reference/importTag4.types @@ -0,0 +1,24 @@ +//// [tests/cases/conformance/jsdoc/importTag4.ts] //// + +=== /types.ts === +export interface Foo { + a: number; +>a : number +} + +=== /foo.js === +/** + * @import { Foo } from "./types" + */ + +/** + * @import { Foo } from "./types" + */ + +/** + * @param { Foo } foo + */ +function f(foo) {} +>f : (foo: Foo) => void +>foo : Foo + diff --git a/tests/baselines/reference/importTag5.js b/tests/baselines/reference/importTag5.js new file mode 100644 index 0000000000000..a4814bf10fdd0 --- /dev/null +++ b/tests/baselines/reference/importTag5.js @@ -0,0 +1,33 @@ +//// [tests/cases/conformance/jsdoc/importTag5.ts] //// + +//// [types.ts] +export interface Foo { + a: number; +} + +//// [foo.js] +/** + * @import { Foo } from "./types" + */ + +/** + * @param { Foo } foo + */ +function f(foo) {} + + + + +//// [types.d.ts] +export interface Foo { + a: number; +} +//// [foo.d.ts] +/** + * @import { Foo } from "./types" + */ +/** + * @param { Foo } foo + */ +declare function f(foo: Foo): void; +import type { Foo } from "./types"; diff --git a/tests/baselines/reference/importTag5.symbols b/tests/baselines/reference/importTag5.symbols new file mode 100644 index 0000000000000..35242e6c8988e --- /dev/null +++ b/tests/baselines/reference/importTag5.symbols @@ -0,0 +1,22 @@ +//// [tests/cases/conformance/jsdoc/importTag5.ts] //// + +=== /types.ts === +export interface Foo { +>Foo : Symbol(Foo, Decl(types.ts, 0, 0)) + + a: number; +>a : Symbol(Foo.a, Decl(types.ts, 0, 22)) +} + +=== /foo.js === +/** + * @import { Foo } from "./types" + */ + +/** + * @param { Foo } foo + */ +function f(foo) {} +>f : Symbol(f, Decl(foo.js, 0, 0)) +>foo : Symbol(foo, Decl(foo.js, 7, 11)) + diff --git a/tests/baselines/reference/importTag5.types b/tests/baselines/reference/importTag5.types new file mode 100644 index 0000000000000..7d9641be55328 --- /dev/null +++ b/tests/baselines/reference/importTag5.types @@ -0,0 +1,20 @@ +//// [tests/cases/conformance/jsdoc/importTag5.ts] //// + +=== /types.ts === +export interface Foo { + a: number; +>a : number +} + +=== /foo.js === +/** + * @import { Foo } from "./types" + */ + +/** + * @param { Foo } foo + */ +function f(foo) {} +>f : (foo: Foo) => void +>foo : Foo + diff --git a/tests/baselines/reference/importTag6.symbols b/tests/baselines/reference/importTag6.symbols new file mode 100644 index 0000000000000..3b917273f6476 --- /dev/null +++ b/tests/baselines/reference/importTag6.symbols @@ -0,0 +1,33 @@ +//// [tests/cases/conformance/jsdoc/importTag6.ts] //// + +=== /types.ts === +export interface A { +>A : Symbol(A, Decl(types.ts, 0, 0)) + + a: number; +>a : Symbol(A.a, Decl(types.ts, 0, 20)) +} +export interface B { +>B : Symbol(B, Decl(types.ts, 2, 1)) + + a: number; +>a : Symbol(B.a, Decl(types.ts, 3, 20)) +} + +=== /foo.js === +/** + * @import { + * A, + * B, + * } from "./types" + */ + +/** + * @param { A } a + * @param { B } b + */ +function f(a, b) {} +>f : Symbol(f, Decl(foo.js, 0, 0)) +>a : Symbol(a, Decl(foo.js, 11, 11)) +>b : Symbol(b, Decl(foo.js, 11, 13)) + diff --git a/tests/baselines/reference/importTag6.types b/tests/baselines/reference/importTag6.types new file mode 100644 index 0000000000000..5adef07704d7e --- /dev/null +++ b/tests/baselines/reference/importTag6.types @@ -0,0 +1,29 @@ +//// [tests/cases/conformance/jsdoc/importTag6.ts] //// + +=== /types.ts === +export interface A { + a: number; +>a : number +} +export interface B { + a: number; +>a : number +} + +=== /foo.js === +/** + * @import { + * A, + * B, + * } from "./types" + */ + +/** + * @param { A } a + * @param { B } b + */ +function f(a, b) {} +>f : (a: * A, b: * B) => void +>a : * A +>b : * B + diff --git a/tests/baselines/reference/importTag7.symbols b/tests/baselines/reference/importTag7.symbols new file mode 100644 index 0000000000000..b4aa8eaf26004 --- /dev/null +++ b/tests/baselines/reference/importTag7.symbols @@ -0,0 +1,32 @@ +//// [tests/cases/conformance/jsdoc/importTag7.ts] //// + +=== /types.ts === +export interface A { +>A : Symbol(A, Decl(types.ts, 0, 0)) + + a: number; +>a : Symbol(A.a, Decl(types.ts, 0, 20)) +} +export interface B { +>B : Symbol(B, Decl(types.ts, 2, 1)) + + a: number; +>a : Symbol(B.a, Decl(types.ts, 3, 20)) +} + +=== /foo.js === +/** + * @import { + * A, + * B } from "./types" + */ + +/** + * @param { A } a + * @param { B } b + */ +function f(a, b) {} +>f : Symbol(f, Decl(foo.js, 0, 0)) +>a : Symbol(a, Decl(foo.js, 10, 11)) +>b : Symbol(b, Decl(foo.js, 10, 13)) + diff --git a/tests/baselines/reference/importTag7.types b/tests/baselines/reference/importTag7.types new file mode 100644 index 0000000000000..f97e3f166e982 --- /dev/null +++ b/tests/baselines/reference/importTag7.types @@ -0,0 +1,28 @@ +//// [tests/cases/conformance/jsdoc/importTag7.ts] //// + +=== /types.ts === +export interface A { + a: number; +>a : number +} +export interface B { + a: number; +>a : number +} + +=== /foo.js === +/** + * @import { + * A, + * B } from "./types" + */ + +/** + * @param { A } a + * @param { B } b + */ +function f(a, b) {} +>f : (a: * A, b: * B) => void +>a : * A +>b : * B + diff --git a/tests/baselines/reference/importTag8.symbols b/tests/baselines/reference/importTag8.symbols new file mode 100644 index 0000000000000..16aae22f55617 --- /dev/null +++ b/tests/baselines/reference/importTag8.symbols @@ -0,0 +1,32 @@ +//// [tests/cases/conformance/jsdoc/importTag8.ts] //// + +=== /types.ts === +export interface A { +>A : Symbol(A, Decl(types.ts, 0, 0)) + + a: number; +>a : Symbol(A.a, Decl(types.ts, 0, 20)) +} +export interface B { +>B : Symbol(B, Decl(types.ts, 2, 1)) + + a: number; +>a : Symbol(B.a, Decl(types.ts, 3, 20)) +} + +=== /foo.js === +/** + * @import + * { A, B } + * from "./types" + */ + +/** + * @param { A } a + * @param { B } b + */ +function f(a, b) {} +>f : Symbol(f, Decl(foo.js, 0, 0)) +>a : Symbol(a, Decl(foo.js, 10, 11)) +>b : Symbol(b, Decl(foo.js, 10, 13)) + diff --git a/tests/baselines/reference/importTag8.types b/tests/baselines/reference/importTag8.types new file mode 100644 index 0000000000000..8494d7cb28c04 --- /dev/null +++ b/tests/baselines/reference/importTag8.types @@ -0,0 +1,28 @@ +//// [tests/cases/conformance/jsdoc/importTag8.ts] //// + +=== /types.ts === +export interface A { + a: number; +>a : number +} +export interface B { + a: number; +>a : number +} + +=== /foo.js === +/** + * @import + * { A, B } + * from "./types" + */ + +/** + * @param { A } a + * @param { B } b + */ +function f(a, b) {} +>f : (a: A, b: B) => void +>a : A +>b : B + diff --git a/tests/baselines/reference/importTag9.symbols b/tests/baselines/reference/importTag9.symbols new file mode 100644 index 0000000000000..dcd2cf14dd9c6 --- /dev/null +++ b/tests/baselines/reference/importTag9.symbols @@ -0,0 +1,32 @@ +//// [tests/cases/conformance/jsdoc/importTag9.ts] //// + +=== /types.ts === +export interface A { +>A : Symbol(A, Decl(types.ts, 0, 0)) + + a: number; +>a : Symbol(A.a, Decl(types.ts, 0, 20)) +} +export interface B { +>B : Symbol(B, Decl(types.ts, 2, 1)) + + a: number; +>a : Symbol(B.a, Decl(types.ts, 3, 20)) +} + +=== /foo.js === +/** + * @import + * * as types + * from "./types" + */ + +/** + * @param { types.A } a + * @param { types.B } b + */ +function f(a, b) {} +>f : Symbol(f, Decl(foo.js, 0, 0)) +>a : Symbol(a, Decl(foo.js, 10, 11)) +>b : Symbol(b, Decl(foo.js, 10, 13)) + diff --git a/tests/baselines/reference/importTag9.types b/tests/baselines/reference/importTag9.types new file mode 100644 index 0000000000000..22aef4498b3ba --- /dev/null +++ b/tests/baselines/reference/importTag9.types @@ -0,0 +1,28 @@ +//// [tests/cases/conformance/jsdoc/importTag9.ts] //// + +=== /types.ts === +export interface A { + a: number; +>a : number +} +export interface B { + a: number; +>a : number +} + +=== /foo.js === +/** + * @import + * * as types + * from "./types" + */ + +/** + * @param { types.A } a + * @param { types.B } b + */ +function f(a, b) {} +>f : (a: types.A, b: types.B) => void +>a : types.A +>b : types.B + diff --git a/tests/baselines/reference/jsdocImportTagCompletion2.baseline b/tests/baselines/reference/jsdocImportTagCompletion2.baseline new file mode 100644 index 0000000000000..e05a6ff4579fd --- /dev/null +++ b/tests/baselines/reference/jsdocImportTagCompletion2.baseline @@ -0,0 +1,48 @@ +// === Completions === +=== /b.js === +// /** +// * @import { } from "./a" +// ^ +// | ---------------------------------------------------------------------- +// | interface A +// | ---------------------------------------------------------------------- +// */ + +[ + { + "marker": { + "fileName": "/b.js", + "position": 17, + "name": "" + }, + "item": { + "flags": 0, + "isGlobalCompletion": false, + "isMemberCompletion": true, + "isNewIdentifierLocation": false, + "entries": [ + { + "name": "A", + "kind": "interface", + "kindModifiers": "export", + "sortText": "11", + "displayParts": [ + { + "text": "interface", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "A", + "kind": "interfaceName" + } + ], + "documentation": [] + } + ] + } + } +] \ No newline at end of file diff --git a/tests/baselines/reference/jsdocImportTagCompletion3.baseline b/tests/baselines/reference/jsdocImportTagCompletion3.baseline new file mode 100644 index 0000000000000..6d25b1ae8f40c --- /dev/null +++ b/tests/baselines/reference/jsdocImportTagCompletion3.baseline @@ -0,0 +1,51 @@ +// === Completions === +=== /tests/cases/fourslash/./c.js === +// /** +// * @import * as types from "./" +// ^ +// | ---------------------------------------------------------------------- +// | a +// | b +// | ---------------------------------------------------------------------- +// */ + +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/./c.js", + "position": 34, + "name": "" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": true, + "entries": [ + { + "name": "a", + "kind": "script", + "kindModifiers": ".ts", + "sortText": "11", + "displayParts": [ + { + "text": "a", + "kind": "text" + } + ] + }, + { + "name": "b", + "kind": "script", + "kindModifiers": ".ts", + "sortText": "11", + "displayParts": [ + { + "text": "b", + "kind": "text" + } + ] + } + ] + } + } +] \ No newline at end of file diff --git a/tests/baselines/reference/jsdocParameterTagSnippetCompletion1.baseline b/tests/baselines/reference/jsdocParameterTagSnippetCompletion1.baseline index 970b5ae25841e..eaa52ac7e7b66 100644 --- a/tests/baselines/reference/jsdocParameterTagSnippetCompletion1.baseline +++ b/tests/baselines/reference/jsdocParameterTagSnippetCompletion1.baseline @@ -40,6 +40,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -136,6 +137,7 @@ // | @host // | @ignore // | @implements +// | @import // | @inheritdoc // | @inner // | @instance @@ -229,6 +231,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -327,6 +330,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -427,6 +431,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -522,6 +527,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -618,6 +624,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -714,6 +721,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -809,6 +817,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -905,6 +914,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -998,6 +1008,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -1092,6 +1103,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -1186,6 +1198,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -1279,6 +1292,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -1374,6 +1388,7 @@ // | @host // | @ignore // | @implements +// | @import // | @inheritdoc // | @inner // | @instance @@ -1467,6 +1482,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -1562,6 +1578,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -2096,6 +2113,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -3215,6 +3245,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -4334,6 +4377,19 @@ ], "documentation": [] }, + { + "name": "@import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "@import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "@inheritdoc", "kind": "", @@ -5440,6 +5496,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -6546,6 +6615,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -7652,6 +7734,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -8771,6 +8866,19 @@ ], "documentation": [] }, + { + "name": "@import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "@import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "@inheritdoc", "kind": "", @@ -9877,6 +9985,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -10983,6 +11104,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -12089,6 +12223,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -13195,6 +13342,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -14301,6 +14461,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -15407,6 +15580,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -16513,6 +16699,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -17632,6 +17831,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -18738,6 +18950,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -19844,6 +20069,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", diff --git a/tests/baselines/reference/jsdocParameterTagSnippetCompletion2.baseline b/tests/baselines/reference/jsdocParameterTagSnippetCompletion2.baseline index 0e8cc9b8ceed1..61f8d6a823f74 100644 --- a/tests/baselines/reference/jsdocParameterTagSnippetCompletion2.baseline +++ b/tests/baselines/reference/jsdocParameterTagSnippetCompletion2.baseline @@ -40,6 +40,7 @@ // | @host // | @ignore // | @implements +// | @import // | @inheritdoc // | @inner // | @instance @@ -133,6 +134,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -231,6 +233,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -323,6 +326,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -416,6 +420,7 @@ // | @host // | @ignore // | @implements +// | @import // | @inheritdoc // | @inner // | @instance @@ -948,6 +953,19 @@ ], "documentation": [] }, + { + "name": "@import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "@import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "@inheritdoc", "kind": "", @@ -2056,6 +2074,19 @@ ], "documentation": [] }, + { + "name": "@import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "@import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "@inheritdoc", "kind": "", @@ -3164,6 +3195,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -4272,6 +4316,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -5380,6 +5437,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", diff --git a/tests/baselines/reference/jsdocParameterTagSnippetCompletion3.baseline b/tests/baselines/reference/jsdocParameterTagSnippetCompletion3.baseline index 31809167ab0f8..e5b6942d334f0 100644 --- a/tests/baselines/reference/jsdocParameterTagSnippetCompletion3.baseline +++ b/tests/baselines/reference/jsdocParameterTagSnippetCompletion3.baseline @@ -40,6 +40,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -131,6 +132,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -223,6 +225,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -318,6 +321,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -413,6 +417,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -506,6 +511,7 @@ // | host // | ignore // | implements +// | import // | inheritdoc // | inner // | instance @@ -1039,6 +1045,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -2145,6 +2164,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -3251,6 +3283,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -4357,6 +4402,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -5463,6 +5521,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", @@ -6569,6 +6640,19 @@ ], "documentation": [] }, + { + "name": "import", + "kind": "", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "import", + "kind": "text" + } + ], + "documentation": [] + }, { "name": "inheritdoc", "kind": "", diff --git a/tests/baselines/reference/renameJsDocImportTag.baseline.jsonc b/tests/baselines/reference/renameJsDocImportTag.baseline.jsonc new file mode 100644 index 0000000000000..69afeb9c6e35a --- /dev/null +++ b/tests/baselines/reference/renameJsDocImportTag.baseline.jsonc @@ -0,0 +1,10 @@ +// === findRenameLocations === +// === /a.js === +// /** +// * <|@import { /*START PREFIX*/A as [|ARENAME|] } from "./b"; +// |>*/ +// +// /** +// * @param { [|ARENAME|]/*RENAME*/ } a +// */ +// function f(a) {} \ No newline at end of file diff --git a/tests/cases/conformance/jsdoc/importTag1.ts b/tests/cases/conformance/jsdoc/importTag1.ts new file mode 100644 index 0000000000000..b85735da4d96b --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag1.ts @@ -0,0 +1,18 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /types.ts +export interface Foo { + a: number; +} + +// @filename: /foo.js +/** + * @import { Foo } from "./types" + */ + +/** + * @param { Foo } foo + */ +function f(foo) {} diff --git a/tests/cases/conformance/jsdoc/importTag10.ts b/tests/cases/conformance/jsdoc/importTag10.ts new file mode 100644 index 0000000000000..ae3f71e74eed4 --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag10.ts @@ -0,0 +1,8 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /foo.js +/** + * @import + */ diff --git a/tests/cases/conformance/jsdoc/importTag11.ts b/tests/cases/conformance/jsdoc/importTag11.ts new file mode 100644 index 0000000000000..f4c2d18ecb7cd --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag11.ts @@ -0,0 +1,8 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /foo.js +/** + * @import foo + */ diff --git a/tests/cases/conformance/jsdoc/importTag12.ts b/tests/cases/conformance/jsdoc/importTag12.ts new file mode 100644 index 0000000000000..464444b8f924c --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag12.ts @@ -0,0 +1,8 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /foo.js +/** + * @import foo from + */ diff --git a/tests/cases/conformance/jsdoc/importTag13.ts b/tests/cases/conformance/jsdoc/importTag13.ts new file mode 100644 index 0000000000000..8f15c41695bb2 --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag13.ts @@ -0,0 +1,11 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /types.ts +export interface Foo { + a: number; +} + +// @filename: /foo.js +/** @import x = require("types") */ diff --git a/tests/cases/conformance/jsdoc/importTag14.ts b/tests/cases/conformance/jsdoc/importTag14.ts new file mode 100644 index 0000000000000..98375d7356c39 --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag14.ts @@ -0,0 +1,6 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /foo.js +/** @import * as f from "./foo" with */ diff --git a/tests/cases/conformance/jsdoc/importTag15.ts b/tests/cases/conformance/jsdoc/importTag15.ts new file mode 100644 index 0000000000000..50004fdb86ac9 --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag15.ts @@ -0,0 +1,15 @@ +// @declaration: true +// @emitDeclarationOnly: true +// @module: esnext,es2015 +// @checkJs: true +// @allowJs: true + +// @filename: 0.ts +export interface I { } + +// @filename: 1.js +/** @import { I } from './0' with { type: "json" } */ +/** @import * as foo from './0' with { type: "json" } */ + +/** @param {I} a */ +function f(a) {} diff --git a/tests/cases/conformance/jsdoc/importTag16.ts b/tests/cases/conformance/jsdoc/importTag16.ts new file mode 100644 index 0000000000000..0a28cd9c8a580 --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag16.ts @@ -0,0 +1,17 @@ +// @declaration: true +// @emitDeclarationOnly: true +// @checkJs: true +// @allowJs: true + +// @filename: a.ts +export default interface Foo {} +export interface I {} + +// @filename: b.js +/** @import Foo, { I } from "./a" */ + +/** + * @param {Foo} a + * @param {I} b + */ +export function foo(a, b) {} diff --git a/tests/cases/conformance/jsdoc/importTag2.ts b/tests/cases/conformance/jsdoc/importTag2.ts new file mode 100644 index 0000000000000..e306afb43a828 --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag2.ts @@ -0,0 +1,18 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /types.ts +export interface Foo { + a: number; +} + +// @filename: /foo.js +/** + * @import * as types from "./types" + */ + +/** + * @param { types.Foo } foo + */ +export function f(foo) {} diff --git a/tests/cases/conformance/jsdoc/importTag3.ts b/tests/cases/conformance/jsdoc/importTag3.ts new file mode 100644 index 0000000000000..7ebe8894b5243 --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag3.ts @@ -0,0 +1,18 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /types.ts +export default interface Foo { + a: number; +} + +// @filename: /foo.js +/** + * @import Foo from "./types" + */ + +/** + * @param { Foo } foo + */ +export function f(foo) {} diff --git a/tests/cases/conformance/jsdoc/importTag4.ts b/tests/cases/conformance/jsdoc/importTag4.ts new file mode 100644 index 0000000000000..1f76717279c5b --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag4.ts @@ -0,0 +1,22 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /types.ts +export interface Foo { + a: number; +} + +// @filename: /foo.js +/** + * @import { Foo } from "./types" + */ + +/** + * @import { Foo } from "./types" + */ + +/** + * @param { Foo } foo + */ +function f(foo) {} diff --git a/tests/cases/conformance/jsdoc/importTag5.ts b/tests/cases/conformance/jsdoc/importTag5.ts new file mode 100644 index 0000000000000..7dfedad464d9b --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag5.ts @@ -0,0 +1,19 @@ +// @checkJs: true +// @allowJs: true +// @declaration: true +// @emitDeclarationOnly: true + +// @filename: /types.ts +export interface Foo { + a: number; +} + +// @filename: /foo.js +/** + * @import { Foo } from "./types" + */ + +/** + * @param { Foo } foo + */ +function f(foo) {} diff --git a/tests/cases/conformance/jsdoc/importTag6.ts b/tests/cases/conformance/jsdoc/importTag6.ts new file mode 100644 index 0000000000000..2d6470c3ff55a --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag6.ts @@ -0,0 +1,25 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /types.ts +export interface A { + a: number; +} +export interface B { + a: number; +} + +// @filename: /foo.js +/** + * @import { + * A, + * B, + * } from "./types" + */ + +/** + * @param { A } a + * @param { B } b + */ +function f(a, b) {} diff --git a/tests/cases/conformance/jsdoc/importTag7.ts b/tests/cases/conformance/jsdoc/importTag7.ts new file mode 100644 index 0000000000000..d6643e411f268 --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag7.ts @@ -0,0 +1,24 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /types.ts +export interface A { + a: number; +} +export interface B { + a: number; +} + +// @filename: /foo.js +/** + * @import { + * A, + * B } from "./types" + */ + +/** + * @param { A } a + * @param { B } b + */ +function f(a, b) {} diff --git a/tests/cases/conformance/jsdoc/importTag8.ts b/tests/cases/conformance/jsdoc/importTag8.ts new file mode 100644 index 0000000000000..9832c5485852e --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag8.ts @@ -0,0 +1,24 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /types.ts +export interface A { + a: number; +} +export interface B { + a: number; +} + +// @filename: /foo.js +/** + * @import + * { A, B } + * from "./types" + */ + +/** + * @param { A } a + * @param { B } b + */ +function f(a, b) {} diff --git a/tests/cases/conformance/jsdoc/importTag9.ts b/tests/cases/conformance/jsdoc/importTag9.ts new file mode 100644 index 0000000000000..6cf733d5e83de --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag9.ts @@ -0,0 +1,24 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @filename: /types.ts +export interface A { + a: number; +} +export interface B { + a: number; +} + +// @filename: /foo.js +/** + * @import + * * as types + * from "./types" + */ + +/** + * @param { types.A } a + * @param { types.B } b + */ +function f(a, b) {} diff --git a/tests/cases/fourslash/autoImportJsDocImport1.ts b/tests/cases/fourslash/autoImportJsDocImport1.ts new file mode 100644 index 0000000000000..65fa9f9506de2 --- /dev/null +++ b/tests/cases/fourslash/autoImportJsDocImport1.ts @@ -0,0 +1,40 @@ +/// + +// @verbatimModuleSyntax: true +// @target: esnext +// @allowJs: true +// @checkJs: true + +// @Filename: /foo.ts +//// export const A = 1; +//// export type B = { x: number }; +//// export type C = 1; +//// export class D { y: string } + +// @Filename: /test.js +/////** +//// * @import { A, D, C } from "./foo" +//// */ +//// +/////** +//// * @param { typeof A } a +//// * @param { B/**/ | C } b +//// * @param { C } c +//// * @param { D } d +//// */ +////export function f(a, b, c, d) { } + +goTo.marker(""); +verify.importFixAtPosition([ +`/** + * @import { A, D, C, B } from "./foo" + */ + +/** + * @param { typeof A } a + * @param { B | C } b + * @param { C } c + * @param { D } d + */ +export function f(a, b, c, d) { }` +]); diff --git a/tests/cases/fourslash/findAllRefsJsDocImportTag.ts b/tests/cases/fourslash/findAllRefsJsDocImportTag.ts new file mode 100644 index 0000000000000..8fb66d013e57f --- /dev/null +++ b/tests/cases/fourslash/findAllRefsJsDocImportTag.ts @@ -0,0 +1,19 @@ +/// + +// @allowJS: true +// @checkJs: true + +// @Filename: /b.ts +////export interface A { } + +// @Filename: /a.js +/////** +//// * @import { A } from "./b"; +//// */ +//// +/////** +//// * @param { [|A/**/|] } a +//// */ +////function f(a) {} + +verify.baselineFindAllReferences(""); diff --git a/tests/cases/fourslash/goToDefinitionJsDocImportTag1.ts b/tests/cases/fourslash/goToDefinitionJsDocImportTag1.ts new file mode 100644 index 0000000000000..56983716b193e --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionJsDocImportTag1.ts @@ -0,0 +1,14 @@ +/// + +// @allowJS: true +// @checkJs: true + +// @Filename: /b.ts +/////*2*/export interface A { } + +// @Filename: /a.js +/////** +//// * @import { A } from [|"./b/*1*/"|] +//// */ + +verify.baselineGoToDefinition("1"); diff --git a/tests/cases/fourslash/goToDefinitionJsDocImportTag2.ts b/tests/cases/fourslash/goToDefinitionJsDocImportTag2.ts new file mode 100644 index 0000000000000..bc8ed4cde99e6 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionJsDocImportTag2.ts @@ -0,0 +1,14 @@ +/// + +// @allowJS: true +// @checkJs: true + +// @Filename: /b.ts +/////*2*/export interface A { } + +// @Filename: /a.js +/////** +//// * @import { A } [|from/*1*/|] "./b" +//// */ + +verify.baselineGoToDefinition("1"); diff --git a/tests/cases/fourslash/goToDefinitionJsDocImportTag3.ts b/tests/cases/fourslash/goToDefinitionJsDocImportTag3.ts new file mode 100644 index 0000000000000..239c42803a1a8 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionJsDocImportTag3.ts @@ -0,0 +1,14 @@ +/// + +// @allowJS: true +// @checkJs: true + +// @Filename: /b.ts +/////*2*/export interface A { } + +// @Filename: /a.js +/////** +//// * @import { A } [|from /*1*/|] "./b"; +//// */ + +verify.baselineGoToDefinition("1"); diff --git a/tests/cases/fourslash/goToDefinitionJsDocImportTag4.ts b/tests/cases/fourslash/goToDefinitionJsDocImportTag4.ts new file mode 100644 index 0000000000000..fd47f3555899b --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionJsDocImportTag4.ts @@ -0,0 +1,14 @@ +/// + +// @allowJS: true +// @checkJs: true + +// @Filename: /b.ts +////export interface /*2*/A { } + +// @Filename: /a.js +/////** +//// * @import { [|A/*1*/|] } from "./b"; +//// */ + +verify.baselineGoToDefinition("1"); diff --git a/tests/cases/fourslash/goToDefinitionJsDocImportTag5.ts b/tests/cases/fourslash/goToDefinitionJsDocImportTag5.ts new file mode 100644 index 0000000000000..fb8a886d5f6af --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionJsDocImportTag5.ts @@ -0,0 +1,19 @@ +/// + +// @allowJS: true +// @checkJs: true + +// @Filename: /b.ts +////export interface /*2*/A { } + +// @Filename: /a.js +/////** +//// * @import { A } from "./b"; +//// */ +//// +/////** +//// * @param { [|A/*1*/|] } a +//// */ +////function f(a) {} + +verify.baselineGoToDefinition("1"); diff --git a/tests/cases/fourslash/jsdocImportTagCompletion1.ts b/tests/cases/fourslash/jsdocImportTagCompletion1.ts new file mode 100644 index 0000000000000..cb7ed23924193 --- /dev/null +++ b/tests/cases/fourslash/jsdocImportTagCompletion1.ts @@ -0,0 +1,13 @@ +/// + +// @allowJS: true +// @checkJs: true + +// @filename: /a.js +/////** +//// * @/**/ +//// */ + +verify.completions( + { marker: "", includes: ["import"] }, +); diff --git a/tests/cases/fourslash/jsdocImportTagCompletion2.ts b/tests/cases/fourslash/jsdocImportTagCompletion2.ts new file mode 100644 index 0000000000000..4a249ba7f6054 --- /dev/null +++ b/tests/cases/fourslash/jsdocImportTagCompletion2.ts @@ -0,0 +1,14 @@ +/// + +// @allowJS: true +// @checkJs: true + +// @filename: /a.ts +////export interface A {} + +// @filename: /b.js +/////** +//// * @import { /**/ } from "./a" +//// */ + +verify.baselineCompletions(); diff --git a/tests/cases/fourslash/jsdocImportTagCompletion3.ts b/tests/cases/fourslash/jsdocImportTagCompletion3.ts new file mode 100644 index 0000000000000..3b84d092aa6d8 --- /dev/null +++ b/tests/cases/fourslash/jsdocImportTagCompletion3.ts @@ -0,0 +1,18 @@ +/// + +// @allowJS: true +// @checkJs: true +// @module: esnext + +// @filename: ./a.ts +////export interface A {} + +// @filename: ./b.ts +////export interface B {} + +// @filename: ./c.js +/////** +//// * @import * as types from ".//**/" +//// */ + +verify.baselineCompletions(); diff --git a/tests/cases/fourslash/refactorConvertImport_namedToDefault2.ts b/tests/cases/fourslash/refactorConvertImport_namedToDefault2.ts new file mode 100644 index 0000000000000..07195e88ef19e --- /dev/null +++ b/tests/cases/fourslash/refactorConvertImport_namedToDefault2.ts @@ -0,0 +1,26 @@ +/// + +// @esModuleInterop: true +// @allowJs: true +// @checkJs: true + +// @Filename: /process.d.ts +////declare module "process" { +//// interface Process { +//// pid: number; +//// addListener(event: string, listener: (...args: any[]) => void): void; +//// } +//// var process: Process; +//// export = process; +////} + +// @Filename: /a.js +/////** [|@import { pid, addListener } from "process"|] */ + +goTo.selectRange(test.ranges()[0]); +edit.applyRefactor({ + refactorName: "Convert import", + actionName: "Convert named imports to default import", + actionDescription: "Convert named imports to default import", + newContent: `/** @import process from "process" */`, +}); diff --git a/tests/cases/fourslash/refactorConvertImport_namedToNamespace10.ts b/tests/cases/fourslash/refactorConvertImport_namedToNamespace10.ts new file mode 100644 index 0000000000000..7fe2e578d8ae8 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertImport_namedToNamespace10.ts @@ -0,0 +1,15 @@ +/// + +// @allowJs: true +// @checkJs: true + +// @filename: /a.js +/////** [|@import { join } from "path"|] */ + +goTo.selectRange(test.ranges()[0]); +edit.applyRefactor({ + refactorName: "Convert import", + actionName: "Convert named imports to namespace import", + actionDescription: "Convert named imports to namespace import", + newContent: `/** @import * as path from "path" */`, +}); diff --git a/tests/cases/fourslash/refactorConvertImport_namespaceToNamed.ts b/tests/cases/fourslash/refactorConvertImport_namespaceToNamed1.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertImport_namespaceToNamed.ts rename to tests/cases/fourslash/refactorConvertImport_namespaceToNamed1.ts diff --git a/tests/cases/fourslash/refactorConvertImport_namespaceToNamed2.ts b/tests/cases/fourslash/refactorConvertImport_namespaceToNamed2.ts new file mode 100644 index 0000000000000..439ab3026fbc1 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertImport_namespaceToNamed2.ts @@ -0,0 +1,31 @@ +/// +// @allowJs: true +// @checkJs: true + +// @filename: /a.js +/////** +//// * [|@import * as types from "types"|] +//// */ +//// +/////** +//// * @param { types.A } a +//// * @param { types.B } b +//// */ +////function f() { } + +goTo.selectRange(test.ranges()[0]); +edit.applyRefactor({ + refactorName: "Convert import", + actionName: "Convert namespace import to named imports", + actionDescription: "Convert namespace import to named imports", + newContent: +`/** + * @import { A, B } from "types" + */ + +/** + * @param { A } a + * @param { B } b + */ +function f() { }`, +}); diff --git a/tests/cases/fourslash/renameJsDocImportTag.ts b/tests/cases/fourslash/renameJsDocImportTag.ts new file mode 100644 index 0000000000000..1e76dc1df8166 --- /dev/null +++ b/tests/cases/fourslash/renameJsDocImportTag.ts @@ -0,0 +1,19 @@ +/// + +// @allowJS: true +// @checkJs: true + +// @Filename: /b.ts +////export interface A { } + +// @Filename: /a.js +/////** +//// * @import { A } from "./b"; +//// */ +//// +/////** +//// * @param { [|A/**/|] } a +//// */ +////function f(a) {} + +verify.baselineRename("");