UNPKG

typedoc

Version:

Create api documentation for TypeScript projects.

1,595 lines (1,583 loc) 830 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); var __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)]; var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn; var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) }); var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]); var __runInitializers = (array, flags, self, value) => { for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value); return value; }; var __decorateElement = (array, flags, name, decorators, target, extra) => { var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16); var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5]; var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []); var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() { return __privateGet(this, extra); }, set [name](x) { return __privateSet(this, extra, x); } }, name)); k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name); for (var i = decorators.length - 1; i >= 0; i--) { ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers); if (k) { ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x }; if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name]; if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y; } it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it); else if (typeof it !== "object" || it === null) __typeError("Object expected"); else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn); } return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target; }; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var __accessCheck = (obj, member2, msg) => member2.has(obj) || __typeError("Cannot " + msg); var __privateIn = (member2, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member2.has(obj); var __privateGet = (obj, member2, getter) => (__accessCheck(obj, member2, "read from private field"), getter ? getter.call(obj) : member2.get(obj)); var __privateAdd = (obj, member2, value) => member2.has(obj) ? __typeError("Cannot add the same private member more than once") : member2 instanceof WeakSet ? member2.add(obj) : member2.set(obj, value); var __privateSet = (obj, member2, value, setter) => (__accessCheck(obj, member2, "write to private field"), setter ? setter.call(obj, value) : member2.set(obj, value), value); var __privateMethod = (obj, member2, method) => (__accessCheck(obj, member2, "access private method"), method); // src/lib/application.ts import * as Path7 from "path"; import ts17 from "typescript"; import { ReflectionSymbolId as ReflectionSymbolId4 } from "#models"; import { AbstractComponent as AbstractComponent5, diagnostic, diagnostics, EntryPointStrategy, FancyConsoleLogger, getCommonDirectory as getCommonDirectory2, getEntryPoints, getLoadedPaths, getPackageDirectories, getWatchEntryPoints, hasBeenLoadedMultipleTimes, inferEntryPoints, isDebugging, loadPlugins, nicePath as nicePath6, normalizePath as normalizePath8, Option as Option16, Options, PackageJsonReader, rootPackageOptions, SUPPORTED_TYPESCRIPT_VERSIONS, TSConfigReader, TYPEDOC_VERSION, TypeDocReader, TYPESCRIPT_ROOT, ValidatingFileRegistry, writeFile as writeFile7 } from "#node-utils"; import { Deserializer, Serializer } from "#serialization"; // src/lib/converter/context.ts import { ok as assert2 } from "assert"; import ts7 from "typescript"; import { Comment as Comment3, ContainerReflection, DeclarationReflection, ReferenceType, ReflectionFlag, ReflectionKind as ReflectionKind3 } from "#models"; // src/lib/converter/utilities/nodes.ts import ts from "typescript"; function isNamedNode(node) { const name = node.name; return !!name && (ts.isMemberName(name) || ts.isComputedPropertyName(name)); } function getHeritageTypes(declarations, kind) { const exprs = declarations.flatMap( (d) => (d.heritageClauses ?? []).filter((hc) => hc.token === kind).flatMap( (hc) => hc.types ) ); const seenTexts = /* @__PURE__ */ new Set(); return exprs.filter((expr) => { const text = expr.getText(); if (seenTexts.has(text)) { return false; } seenTexts.add(text); return true; }); } function isObjectType(type2) { return typeof type2.objectFlags === "number"; } function isTypeReference(type2) { return isObjectType(type2) && (type2.objectFlags & ts.ObjectFlags.Reference) !== 0; } // src/lib/converter/converter-events.ts var ConverterEvents = { BEGIN: "begin", END: "end", CREATE_PROJECT: "createProject", CREATE_DECLARATION: "createDeclaration", CREATE_DOCUMENT: "createDocument", CREATE_SIGNATURE: "createSignature", CREATE_PARAMETER: "createParameter", CREATE_TYPE_PARAMETER: "createTypeParameter", RESOLVE_BEGIN: "resolveBegin", RESOLVE: "resolveReflection", RESOLVE_END: "resolveEnd" }; // src/lib/converter/utilities/symbols.ts import ts2 from "typescript"; function resolveAliasedSymbol(symbol, checker) { const seen = /* @__PURE__ */ new Set(); while (ts2.SymbolFlags.Alias & symbol.flags) { symbol = checker.getAliasedSymbol(symbol); if (seen.has(symbol)) return symbol; seen.add(symbol); } return symbol; } // src/lib/converter/comments/index.ts import { Comment as Comment2, ReflectionKind as ReflectionKind2 } from "#models"; import { assertNever as assertNever3, i18n as i18n4, Logger, setUnion } from "#utils"; import ts5 from "typescript"; // src/lib/converter/comments/blockLexer.ts import ts3 from "typescript"; function* lexBlockComment(file, pos = 0, end = file.length, createSymbolId = () => { throw new Error("unreachable"); }, jsDoc, checker) { let textToken; for (const token of lexBlockComment2( file, pos, end, getLinkTags(jsDoc), checker, createSymbolId )) { if (token.kind === "text" /* Text */) { if (textToken) { textToken.text += token.text; } else { textToken = token; } } else { if (textToken) { yield textToken; textToken = void 0; } yield token; } } if (textToken) { yield textToken; } return; } function getLinkTags(jsDoc) { const result = []; if (jsDoc?.comment && typeof jsDoc.comment !== "string") { for (const part of jsDoc.comment) { switch (part.kind) { case ts3.SyntaxKind.JSDocLink: case ts3.SyntaxKind.JSDocLinkCode: case ts3.SyntaxKind.JSDocLinkPlain: result.push(part); } } } for (const block of jsDoc?.tags || []) { if (!block.comment || typeof block.comment === "string") continue; for (const part of block.comment) { switch (part.kind) { case ts3.SyntaxKind.JSDocLink: case ts3.SyntaxKind.JSDocLinkCode: case ts3.SyntaxKind.JSDocLinkPlain: result.push(part); } } } return result; } function* lexBlockComment2(file, pos, end, linkTags2, checker, createSymbolId) { pos += 2; end -= 2; if (pos < end && file[pos] === "*") { pos++; } const [commentHasStars, indent2] = discoverIndent(file, pos, end); while (pos < end && /\s/.test(file[pos])) { pos++; } while (pos < end && /\s/.test(file[end - 1])) { end--; } let lineStart = true; let braceStartsType = false; let linkTagIndex = 0; for (; ; ) { if (pos >= end) { return; } if (lineStart) { pos = skipIndent(pos); if (commentHasStars && file[pos] === "*") { pos++; if (file[pos] === " ") { pos++; } } lineStart = false; } switch (file[pos]) { case "\n": yield makeToken("new_line" /* NewLine */, 1); lineStart = true; break; case "{": if (braceStartsType && nextNonWs(pos + 1) !== "@") { yield makeToken( "type" /* TypeAnnotation */, findEndOfType(pos) - pos ); braceStartsType = false; } else { yield makeToken("open_brace" /* OpenBrace */, 1); } break; case "}": yield makeToken("close_brace" /* CloseBrace */, 1); braceStartsType = false; break; case "`": { braceStartsType = false; let tickCount = 1; let lookahead = pos - 1; let atNewline = true; while (lookahead > 0 && file[lookahead] !== "\n") { if (/\S/.test(file[lookahead])) { if (!commentHasStars || file[lookahead] !== "*") { atNewline = false; break; } } --lookahead; } lookahead = pos; while (lookahead + 1 < end && file[lookahead + 1] === "`") { tickCount++; lookahead++; } const isCodeBlock = atNewline && tickCount >= 3; let lookaheadStart = pos; const codeText = []; lookahead++; while (lookahead < end) { if (lookaheadExactlyNTicks(lookahead, tickCount)) { lookahead += tickCount; codeText.push( file.substring(lookaheadStart, lookahead) ); const codeTextStr = codeText.join(""); if (isCodeBlock || !/\n\s*\n/.test(codeTextStr)) { yield { kind: "code" /* Code */, text: codeTextStr, pos }; pos = lookahead; } else { yield makeToken("text" /* Text */, tickCount); } break; } else if (file[lookahead] === "`") { while (lookahead < end && file[lookahead] === "`") { lookahead++; } } else if (file[lookahead] === "\\" && lookahead + 1 < end && file[lookahead + 1] === "/") { codeText.push( file.substring(lookaheadStart, lookahead) ); lookaheadStart = lookahead + 1; lookahead += 2; } else if (file[lookahead] === "\\" && lookahead + 1 < end && file[lookahead + 1] !== "\n") { lookahead += 2; } else if (file[lookahead] === "\n") { lookahead++; codeText.push( file.substring(lookaheadStart, lookahead) ); lookahead = skipIndent(lookahead); if (commentHasStars && file[lookahead] === "*") { lookahead++; if (file[lookahead] === " ") { lookahead++; } } lookaheadStart = lookahead; } else { lookahead++; } } if (lookahead >= end && pos !== lookahead) { if (isCodeBlock && file.substring(pos, end).includes("\n")) { codeText.push(file.substring(lookaheadStart, end)); yield { kind: "code" /* Code */, text: codeText.join(""), pos }; pos = lookahead; } else { yield makeToken("text" /* Text */, tickCount); } } break; } case "@": { let lookahead = pos + 1; while (lookahead < end && /[a-z]/i.test(file[lookahead])) { lookahead++; } if (lookahead !== pos + 1) { while (lookahead < end && /[a-z0-9-]/i.test(file[lookahead])) { lookahead++; } } if (lookahead !== pos + 1 && (lookahead === end || /[\s}]/.test(file[lookahead]))) { braceStartsType = true; const token = makeToken( "tag" /* Tag */, lookahead - pos ); attachLinkTagResult(token); yield token; break; } } // fall through if we didn't find something that looks like a tag default: { const textParts = []; let lookaheadStart = pos; let lookahead = pos; while (lookahead < end) { if ("{}\n`".includes(file[lookahead])) break; if (lookahead !== pos && file[lookahead] === "@" && /\s/.test(file[lookahead - 1])) { break; } if (file[lookahead] === "\\" && lookahead + 1 < end && "{}@/`".includes(file[lookahead + 1])) { textParts.push( file.substring(lookaheadStart, lookahead), file[lookahead + 1] ); lookahead++; lookaheadStart = lookahead + 1; } lookahead++; } textParts.push(file.substring(lookaheadStart, lookahead)); if (textParts.some((part) => /\S/.test(part))) { braceStartsType = false; } yield { kind: "text" /* Text */, text: textParts.join(""), pos }; pos = lookahead; break; } } } function attachLinkTagResult(token) { while (linkTagIndex < linkTags2.length && linkTags2[linkTagIndex].pos < token.pos - 1) { linkTagIndex++; } if (linkTagIndex < linkTags2.length && linkTags2[linkTagIndex].pos === token.pos - 1) { const link = linkTags2[linkTagIndex]; if (link.name) { const tsTarget = checker?.getSymbolAtLocation( getRightmostName(link.name) ); if (tsTarget) { token.tsLinkTarget = createSymbolId( resolveAliasedSymbol(tsTarget, checker) ); token.tsLinkText = link.text.replace(/^\s*\|\s*/, ""); } } } } function makeToken(kind, size) { const start = pos; pos += size; return { kind, text: file.substring(start, pos), pos: start }; } function skipIndent(pos2) { let taken = indent2; let lookahead = pos2; while (taken > 0 && lookahead < end && file[lookahead] !== "\n" && /\s/.test(file[lookahead])) { taken--; lookahead++; } return lookahead; } function lookaheadExactlyNTicks(pos2, n) { if (pos2 + n > end) { return false; } return file.startsWith("`".repeat(n), pos2) && file[pos2 + n] !== "`"; } function findEndOfType(pos2) { let openBraces = 0; while (pos2 < end) { if (file[pos2] === "{") { openBraces++; } else if (file[pos2] === "}") { if (--openBraces === 0) { break; } } else if ("`'\"".includes(file[pos2])) { pos2 = findEndOfString(pos2); } pos2++; } if (pos2 < end && file[pos2] === "}") { pos2++; } return pos2; } function findEndOfString(pos2) { const endOfString = file[pos2]; pos2++; while (pos2 < end) { if (file[pos2] === endOfString) { break; } else if (file[pos2] === "\\") { pos2++; } else if (endOfString === "`" && file[pos2] === "$" && file[pos2 + 1] === "{") { while (pos2 < end && file[pos2] !== "}") { if ("`'\"".includes(file[pos2])) { pos2 = findEndOfString(pos2) + 1; } else { pos2++; } } } pos2++; } return pos2; } function nextNonWs(pos2) { while (pos2 < end && /\s/.test(file[pos2])) { pos2++; } return file[pos2]; } } function discoverIndent(file, pos, end) { let indent2 = 0; while (pos < end && file[pos] !== "\n") { pos++; } outer: while (pos < end) { pos++; const lineStart = pos; while (pos < end && file[pos] !== "\n") { if (/\S/.test(file[pos])) { indent2 = pos - lineStart; break outer; } pos++; } } const commentHasStars = pos < end && file[pos] === "*"; return [commentHasStars, indent2]; } function getRightmostName(name) { if (ts3.isJSDocMemberName(name)) { return name.right; } if (ts3.isQualifiedName(name)) { return name.right; } return name; } // src/lib/converter/comments/discovery.ts import { ReflectionKind } from "#models"; import { CommentStyle, nicePath } from "#node-utils"; import { assertNever, filter, firstDefined, i18n } from "#utils"; import { ok } from "assert"; import ts4 from "typescript"; var variablePropertyKinds = [ ts4.SyntaxKind.PropertyDeclaration, ts4.SyntaxKind.PropertySignature, ts4.SyntaxKind.BinaryExpression, ts4.SyntaxKind.PropertyAssignment, ts4.SyntaxKind.ShorthandPropertyAssignment, // class X { constructor(/** Comment */ readonly z: string) } ts4.SyntaxKind.Parameter, // Variable values ts4.SyntaxKind.VariableDeclaration, ts4.SyntaxKind.BindingElement, ts4.SyntaxKind.ExportAssignment, ts4.SyntaxKind.PropertyAccessExpression ]; var wantedKinds = { [ReflectionKind.Project]: [ ts4.SyntaxKind.SourceFile, ts4.SyntaxKind.ModuleDeclaration ], [ReflectionKind.Module]: [ ts4.SyntaxKind.SourceFile, ts4.SyntaxKind.ModuleDeclaration ], [ReflectionKind.Namespace]: [ ts4.SyntaxKind.ModuleDeclaration, ts4.SyntaxKind.SourceFile, ts4.SyntaxKind.BindingElement, ts4.SyntaxKind.ExportSpecifier, ts4.SyntaxKind.NamespaceExport ], [ReflectionKind.Enum]: [ ts4.SyntaxKind.EnumDeclaration ], [ReflectionKind.EnumMember]: [ ts4.SyntaxKind.EnumMember ], [ReflectionKind.Variable]: variablePropertyKinds, [ReflectionKind.Function]: [ ts4.SyntaxKind.FunctionDeclaration, ts4.SyntaxKind.BindingElement, ts4.SyntaxKind.VariableDeclaration, ts4.SyntaxKind.ExportAssignment, ts4.SyntaxKind.PropertyAccessExpression, ts4.SyntaxKind.PropertyDeclaration, ts4.SyntaxKind.PropertyAssignment, ts4.SyntaxKind.ShorthandPropertyAssignment ], [ReflectionKind.Class]: [ ts4.SyntaxKind.ClassDeclaration, ts4.SyntaxKind.BindingElement ], [ReflectionKind.Interface]: [ ts4.SyntaxKind.InterfaceDeclaration ], [ReflectionKind.Constructor]: [ts4.SyntaxKind.Constructor], [ReflectionKind.Property]: variablePropertyKinds, [ReflectionKind.Method]: [ ts4.SyntaxKind.FunctionDeclaration, ts4.SyntaxKind.MethodDeclaration ], [ReflectionKind.CallSignature]: [ ts4.SyntaxKind.FunctionDeclaration, ts4.SyntaxKind.VariableDeclaration, ts4.SyntaxKind.MethodDeclaration, ts4.SyntaxKind.MethodDeclaration, ts4.SyntaxKind.PropertyDeclaration, ts4.SyntaxKind.PropertySignature, ts4.SyntaxKind.CallSignature ], [ReflectionKind.IndexSignature]: [ts4.SyntaxKind.IndexSignature], [ReflectionKind.ConstructorSignature]: [ts4.SyntaxKind.ConstructSignature], [ReflectionKind.Parameter]: [ts4.SyntaxKind.Parameter], [ReflectionKind.TypeLiteral]: [ts4.SyntaxKind.TypeLiteral], [ReflectionKind.TypeParameter]: [ts4.SyntaxKind.TypeParameter], [ReflectionKind.Accessor]: [ts4.SyntaxKind.PropertyDeclaration], [ReflectionKind.GetSignature]: [ts4.SyntaxKind.GetAccessor], [ReflectionKind.SetSignature]: [ts4.SyntaxKind.SetAccessor], [ReflectionKind.TypeAlias]: [ ts4.SyntaxKind.TypeAliasDeclaration ], [ReflectionKind.Reference]: [ ts4.SyntaxKind.NamespaceExport, ts4.SyntaxKind.ExportSpecifier ], // Non-TS kind, will never have comments. [ReflectionKind.Document]: [] }; var backupWantedKinds = { [ReflectionKind.Project]: [], [ReflectionKind.Module]: [], [ReflectionKind.Namespace]: [ // @namespace support ts4.SyntaxKind.VariableDeclaration, ts4.SyntaxKind.BindingElement, ts4.SyntaxKind.ExportAssignment, ts4.SyntaxKind.PropertyAccessExpression, ts4.SyntaxKind.PropertyDeclaration, ts4.SyntaxKind.PropertyAssignment, ts4.SyntaxKind.ShorthandPropertyAssignment ], [ReflectionKind.Enum]: [ ts4.SyntaxKind.VariableDeclaration ], [ReflectionKind.EnumMember]: [ // These here so that @enum gets comments ts4.SyntaxKind.PropertyAssignment, ts4.SyntaxKind.PropertySignature ], [ReflectionKind.Variable]: [], [ReflectionKind.Function]: [ ts4.SyntaxKind.FunctionDeclaration, ts4.SyntaxKind.BindingElement, ts4.SyntaxKind.VariableDeclaration, ts4.SyntaxKind.ExportAssignment, ts4.SyntaxKind.PropertyAccessExpression, ts4.SyntaxKind.PropertyDeclaration, ts4.SyntaxKind.PropertyAssignment, ts4.SyntaxKind.ShorthandPropertyAssignment ], [ReflectionKind.Class]: [ // If marked with @class ts4.SyntaxKind.VariableDeclaration, ts4.SyntaxKind.ExportAssignment, ts4.SyntaxKind.FunctionDeclaration ], [ReflectionKind.Interface]: [ ts4.SyntaxKind.TypeAliasDeclaration, ts4.SyntaxKind.ClassDeclaration // type only exports ], [ReflectionKind.Constructor]: [], [ReflectionKind.Property]: [], [ReflectionKind.Method]: [], [ReflectionKind.CallSignature]: [], [ReflectionKind.IndexSignature]: [], [ReflectionKind.ConstructorSignature]: [], [ReflectionKind.Parameter]: [], [ReflectionKind.TypeLiteral]: [], [ReflectionKind.TypeParameter]: [], [ReflectionKind.Accessor]: [], [ReflectionKind.GetSignature]: [], [ReflectionKind.SetSignature]: [], [ReflectionKind.TypeAlias]: [ ts4.SyntaxKind.FunctionDeclaration, // type only exports ts4.SyntaxKind.VariableDeclaration // type only exports ], [ReflectionKind.Reference]: [], // Non-TS kind, will never have comments. [ReflectionKind.Document]: [] }; function discoverFileComments(node, commentStyle) { const text = node.text; const comments = collectCommentRanges( ts4.getLeadingCommentRanges(text, node.pos) ); const selectedDocComments = comments.filter((ranges) => permittedRange(text, ranges, commentStyle)); return selectedDocComments.map((ranges) => { return { file: node, ranges, jsDoc: findJsDocForComment(node, ranges), inheritedFromParentDeclaration: false }; }); } function discoverNodeComment(node, commentStyle) { const text = node.getSourceFile().text; const comments = collectCommentRanges( ts4.getLeadingCommentRanges(text, node.pos) ); comments.reverse(); const selectedDocComment = comments.find((ranges) => permittedRange(text, ranges, commentStyle)); if (selectedDocComment) { return { file: node.getSourceFile(), ranges: selectedDocComment, jsDoc: findJsDocForComment(node, selectedDocComment), inheritedFromParentDeclaration: false }; } } function checkCommentDeclarations(commentNodes, reverse, commentStyle) { const discovered = []; for (const { node, inheritedFromParentDeclaration } of commentNodes) { const text = node.getSourceFile().text; const comments = collectCommentRanges( ts4.getLeadingCommentRanges(text, node.pos) ); if (reverse) { comments.reverse(); } const selectedDocComment = comments.find((ranges) => permittedRange(text, ranges, commentStyle)); if (selectedDocComment) { discovered.push({ file: node.getSourceFile(), ranges: selectedDocComment, jsDoc: findJsDocForComment(node, selectedDocComment), inheritedFromParentDeclaration }); } } return discovered; } function discoverComment(symbol, kind, logger, commentStyle, checker, declarationWarnings) { const discovered = discoverCommentWorker( symbol, kind, logger, commentStyle, checker, declarationWarnings, wantedKinds[kind] ); if (discovered) { return discovered; } return discoverCommentWorker( symbol, kind, logger, commentStyle, checker, declarationWarnings, backupWantedKinds[kind] ); } function discoverCommentWorker(symbol, kind, logger, commentStyle, checker, declarationWarnings, wanted) { if (wanted.length === 0) { return; } const reverse = !symbol.declarations?.some(ts4.isSourceFile); const wantedDeclarations = filter(symbol.declarations, (decl) => wanted.includes(decl.kind)); const commentNodes = wantedDeclarations.flatMap((decl) => declarationToCommentNodes(decl, checker)); if (kind & ReflectionKind.ContainsCallSignatures) { const canHaveOverloads = wantedDeclarations.some( (node) => [ ts4.SyntaxKind.FunctionDeclaration, ts4.SyntaxKind.MethodDeclaration, ts4.SyntaxKind.Constructor ].includes(node.kind) ); const isOverloaded = canHaveOverloads && wantedDeclarations.length > 1; if (isOverloaded) { commentNodes.length = 0; const implementationNode = wantedDeclarations.find( (node) => node.body ); if (implementationNode) { commentNodes.push({ node: implementationNode, inheritedFromParentDeclaration: false }); } } else if (canHaveOverloads) { commentNodes.length = 0; } else { } } const discovered = checkCommentDeclarations( commentNodes, reverse, commentStyle ); switch (discovered.length) { case 0: return void 0; case 1: return discovered[0]; default: { if (discovered.filter((n) => !n.inheritedFromParentDeclaration).length > 1 && (declarationWarnings || discovered.some((dc) => !dc.file.isDeclarationFile))) { logger.warn( i18n.symbol_0_has_multiple_declarations_with_comment( symbol.name ) ); const locations = discovered.map( ({ file, ranges: [{ pos }] }) => { const path3 = nicePath(file.fileName); const line2 = ts4.getLineAndCharacterOfPosition(file, pos).line + 1; return `${path3}:${line2}`; } ); logger.info( i18n.comments_for_0_are_declared_at_1( symbol.name, locations.join("\n ") ) ); } return discovered[0]; } } } function discoverSignatureComment(declaration, checker, commentStyle) { for (const { node, inheritedFromParentDeclaration } of declarationToCommentNodes(declaration, checker)) { if (ts4.isJSDocSignature(node)) { const comment2 = node.parent.parent; ok(ts4.isJSDoc(comment2)); return { file: node.getSourceFile(), ranges: [ { kind: ts4.SyntaxKind.MultiLineCommentTrivia, pos: comment2.pos, end: comment2.end } ], jsDoc: comment2, inheritedFromParentDeclaration }; } const text = node.getSourceFile().text; const comments = collectCommentRanges( ts4.getLeadingCommentRanges(text, node.pos) ); comments.reverse(); const comment = comments.find((ranges) => permittedRange(text, ranges, commentStyle)); if (comment) { return { file: node.getSourceFile(), ranges: comment, jsDoc: findJsDocForComment(node, comment), inheritedFromParentDeclaration }; } } } function findJsDocForComment(node, ranges) { if (ranges[0].kind === ts4.SyntaxKind.MultiLineCommentTrivia) { const jsDocs = ts4.getJSDocCommentsAndTags(node).map((doc) => ts4.findAncestor(doc, ts4.isJSDoc)); if (ts4.isSourceFile(node)) { if (node.statements.length) { jsDocs.push(...node.statements[0].getChildren().filter(ts4.isJSDoc)); } } return jsDocs.find((doc) => doc.pos === ranges[0].pos); } } function isTopmostModuleDeclaration(node) { return node.getChildren().some(ts4.isModuleBlock); } function getRootModuleDeclaration(node) { while (node.parent.kind === ts4.SyntaxKind.ModuleDeclaration) { const parent = node.parent; if (node.name.pos === parent.name.end + 1) { node = parent; } else { break; } } return node; } function declarationToCommentNodeIgnoringParents(node) { if (!node.parent) return node; if (node.kind === ts4.SyntaxKind.Parameter) { return node; } if (node.parent.kind === ts4.SyntaxKind.VariableDeclarationList) { return node.parent.parent; } if (node.parent.kind === ts4.SyntaxKind.VariableDeclaration) { return node.parent.parent.parent; } if ([ ts4.SyntaxKind.PropertyDeclaration, ts4.SyntaxKind.BinaryExpression, ts4.SyntaxKind.ExportAssignment ].includes(node.parent.kind)) { return node.parent; } if (ts4.isModuleDeclaration(node)) { if (!isTopmostModuleDeclaration(node)) { return; } else { return getRootModuleDeclaration(node); } } if (node.kind === ts4.SyntaxKind.ExportSpecifier) { return node.parent.parent; } if (ts4.SyntaxKind.NamespaceExport === node.kind) { return node.parent; } } function declarationToCommentNodes(node, checker) { const commentNode = declarationToCommentNodeIgnoringParents(node); if (commentNode) { return [ { node: commentNode, inheritedFromParentDeclaration: false } ]; } const result = [ { node, inheritedFromParentDeclaration: false } ]; let overloadIndex = void 0; if (ts4.isMethodDeclaration(node)) { const symbol = checker.getSymbolAtLocation(node.name || node); if (symbol) { overloadIndex = symbol.declarations?.filter((d) => d.kind === node.kind).indexOf(node); ok(overloadIndex !== -1, "Should always find declaration"); } } const seenSymbols = /* @__PURE__ */ new Set(); const bases = findBaseOfDeclaration(checker, node, (symbol) => { if (!seenSymbols.has(symbol)) { seenSymbols.add(symbol); if (overloadIndex === void 0) { return symbol.declarations?.map( (node2) => declarationToCommentNodeIgnoringParents(node2) || node2 ); } else if (symbol.declarations?.[overloadIndex]) { const parentSigNode = symbol.declarations[overloadIndex]; return [ declarationToCommentNodeIgnoringParents(parentSigNode) || parentSigNode ]; } } }); for (const parentCommentNode of bases || []) { result.push({ node: parentCommentNode, inheritedFromParentDeclaration: true }); } if (ts4.isShorthandPropertyAssignment(node)) { const sourceSymbol = checker.getShorthandAssignmentValueSymbol(node); if (sourceSymbol?.valueDeclaration) { const commentNode2 = declarationToCommentNodeIgnoringParents(sourceSymbol.valueDeclaration); if (commentNode2) { result.push( { node: commentNode2, inheritedFromParentDeclaration: true } ); } } const originalSymbol = sourceSymbol && resolveAliasedSymbol(sourceSymbol, checker); if (originalSymbol !== sourceSymbol && originalSymbol?.valueDeclaration) { const commentNode2 = declarationToCommentNodeIgnoringParents(originalSymbol?.valueDeclaration); if (commentNode2) { result.push( { node: commentNode2, inheritedFromParentDeclaration: true } ); } } } if ((ts4.isFunctionDeclaration(node) || ts4.isMethodDeclaration(node)) && node.name) { const symbol = checker.getSymbolAtLocation(node.name); if (symbol && symbol.declarations[0] !== node) { result.push({ node: symbol.declarations[0], inheritedFromParentDeclaration: true }); } } return result; } function findBaseOfDeclaration(checker, declaration, cb) { const classOrInterfaceDeclaration = declaration.parent?.kind === ts4.SyntaxKind.Constructor ? declaration.parent.parent : declaration.parent; if (!classOrInterfaceDeclaration) return; const isStaticMember = ts4.getCombinedModifierFlags(declaration) & ts4.ModifierFlags.Static; return firstDefined( ts4.getAllSuperTypeNodes(classOrInterfaceDeclaration), (superTypeNode) => { const baseType = checker.getTypeAtLocation(superTypeNode); const type2 = isStaticMember && baseType.symbol ? checker.getTypeOfSymbol(baseType.symbol) : baseType; const symbol = checker.getPropertyOfType( type2, declaration.symbol.name ); return symbol ? cb(symbol) : void 0; } ); } function collectCommentRanges(ranges) { const result = []; let collect = []; for (const range of ranges || []) { collect.push(range); switch (range.kind) { case ts4.SyntaxKind.MultiLineCommentTrivia: if (collect.length) { result.push(collect); collect = []; } result.push([range]); break; case ts4.SyntaxKind.SingleLineCommentTrivia: collect.push(range); break; /* istanbul ignore next */ default: assertNever(range.kind); } } if (collect.length) { result.push(collect); } return result; } function permittedRange(text, ranges, commentStyle) { switch (commentStyle) { case CommentStyle.All: return true; case CommentStyle.Block: return ranges[0].kind === ts4.SyntaxKind.MultiLineCommentTrivia; case CommentStyle.Line: return ranges[0].kind === ts4.SyntaxKind.SingleLineCommentTrivia; case CommentStyle.TripleSlash: return ranges[0].kind === ts4.SyntaxKind.SingleLineCommentTrivia && text[ranges[0].pos + 2] === "/" && text[ranges[0].pos + 3] !== "/"; case CommentStyle.JSDoc: return ranges[0].kind === ts4.SyntaxKind.MultiLineCommentTrivia && text[ranges[0].pos] === "/" && text[ranges[0].pos + 1] === "*" && text[ranges[0].pos + 2] === "*"; } } // src/lib/converter/comments/lineLexer.ts function* lexLineComments(file, ranges) { let textToken; for (const token of lexLineComments2( file, ranges[0].pos, ranges[ranges.length - 1].end )) { if (token.kind === "text" /* Text */) { if (textToken) { textToken.text += token.text; } else { textToken = token; } } else { if (textToken) { yield textToken; textToken = void 0; } yield token; } } if (textToken) { yield textToken; } return; } function* lexLineComments2(file, pos, end) { while (pos < end && /\s/.test(file[end - 1])) { end--; } let lineStart = true; let braceStartsType = false; for (; ; ) { if (lineStart) { pos = skipLeadingLineTrivia(pos); lineStart = false; } if (pos >= end) { return; } switch (file[pos]) { case "\n": yield makeToken("new_line" /* NewLine */, 1); lineStart = true; break; case "{": if (braceStartsType && nextNonWs(pos + 1) !== "@") { yield makeToken( "type" /* TypeAnnotation */, findEndOfType(pos) - pos ); braceStartsType = false; } else { yield makeToken("open_brace" /* OpenBrace */, 1); } break; case "}": yield makeToken("close_brace" /* CloseBrace */, 1); braceStartsType = false; break; case "`": { braceStartsType = false; let tickCount = 1; let lookahead = pos - 1; let atNewline = true; while (lookahead > 0 && file[lookahead] !== "\n") { if (/\S/.test(file[lookahead])) { atNewline = false; break; } --lookahead; } lookahead = pos; while (lookahead + 1 < end && file[lookahead + 1] === "`") { tickCount++; lookahead++; } const isCodeBlock = atNewline && tickCount >= 3; let lookaheadStart = pos; const codeText = []; lookahead++; while (lookahead < end) { if (lookaheadExactlyNTicks(lookahead, tickCount)) { lookahead += tickCount; codeText.push( file.substring(lookaheadStart, lookahead) ); const codeTextStr = codeText.join(""); if (isCodeBlock || !/\n\s*\n/.test(codeTextStr)) { yield { kind: "code" /* Code */, text: codeTextStr, pos }; pos = lookahead; } else { yield makeToken("text" /* Text */, tickCount); } break; } else if (file[lookahead] === "`") { while (lookahead < end && file[lookahead] === "`") { lookahead++; } } else if (file[lookahead] === "\\" && lookahead + 1 < end && file[lookahead + 1] !== "\n") { lookahead += 2; } else if (file[lookahead] === "\n") { lookahead++; codeText.push( file.substring(lookaheadStart, lookahead) ); lookahead = skipLeadingLineTrivia(lookahead); lookaheadStart = lookahead; } else { lookahead++; } } if (lookahead >= end && pos !== lookahead) { if (tickCount === 3 && file.substring(pos, end).includes("\n")) { codeText.push(file.substring(lookaheadStart, end)); yield { kind: "code" /* Code */, text: codeText.join(""), pos }; pos = lookahead; } else { yield makeToken("text" /* Text */, tickCount); } } break; } case "@": { let lookahead = pos + 1; while (lookahead < end && /[a-z]/i.test(file[lookahead])) { lookahead++; } if (lookahead !== pos + 1) { while (lookahead < end && /[a-z0-9-]/i.test(file[lookahead])) { lookahead++; } } if (lookahead !== pos + 1 && (lookahead === end || /[\s}]/.test(file[lookahead]))) { braceStartsType = true; yield makeToken("tag" /* Tag */, lookahead - pos); break; } } // fall through if we didn't find something that looks like a tag default: { const textParts = []; let lookaheadStart = pos; let lookahead = pos; while (lookahead < end) { if ("{}\n`".includes(file[lookahead])) break; if (lookahead !== pos && file[lookahead] === "@" && /\s/.test(file[lookahead - 1])) { break; } if (file[lookahead] === "\\" && lookahead + 1 < end && "{}@`".includes(file[lookahead + 1])) { textParts.push( file.substring(lookaheadStart, lookahead), file[lookahead + 1] ); lookahead++; lookaheadStart = lookahead + 1; } lookahead++; } textParts.push(file.substring(lookaheadStart, lookahead)); if (textParts.some((part) => /\S/.test(part))) { braceStartsType = false; } yield { kind: "text" /* Text */, text: textParts.join(""), pos }; pos = lookahead; break; } } } function makeToken(kind, size) { const start = pos; pos += size; return { kind, text: file.substring(start, pos), pos: start }; } function skipLeadingLineTrivia(pos2) { let lookahead = pos2; while (lookahead < end && /\s/.test(file[lookahead])) { lookahead++; } while (lookahead < end && file[lookahead] === "/") { lookahead++; } if (lookahead < end && file[lookahead] === " ") { lookahead++; } return lookahead; } function lookaheadExactlyNTicks(pos2, n) { if (pos2 + n > end) { return false; } return file.startsWith("`".repeat(n), pos2) && file[pos2 + n] !== "`"; } function findEndOfType(pos2) { let openBraces = 0; while (pos2 < end) { if (file[pos2] === "{") { openBraces++; } else if (file[pos2] === "}") { if (--openBraces === 0) { break; } } else if ("`'\"".includes(file[pos2])) { pos2 = findEndOfString(pos2); } pos2++; } if (pos2 < end && file[pos2] === "}") { pos2++; } return pos2; } function findEndOfString(pos2) { const endOfString = file[pos2]; pos2++; while (pos2 < end) { if (file[pos2] === endOfString) { break; } else if (file[pos2] === "\\") { pos2++; } else if (endOfString === "`" && file[pos2] === "$" && file[pos2 + 1] === "{") { while (pos2 < end && file[pos2] !== "}") { if ("`'\"".includes(file[pos2])) { pos2 = findEndOfString(pos2) + 1; } else { pos2++; } } } pos2++; } return pos2; } function nextNonWs(pos2) { while (pos2 < end && /\s/.test(file[pos2])) { pos2++; } return file[pos2]; } } // src/lib/converter/comments/parser.ts import { Comment, CommentTag, FileRegistry } from "#models"; import { hasDeclarationFileExtension, nicePath as nicePath2 } from "#node-utils"; import { assertNever as assertNever2, i18n as i18n3, removeIf } from "#utils"; import assert, { ok as ok2 } from "assert"; import { parseDocument as parseYamlDoc } from "yaml"; // src/lib/converter/comments/tagName.ts function extractTagName(text) { let pos = skipWs(text, 0); let nameStart = pos; let bracketDepth = 0; let stringChar = ""; while (pos < text.length && (bracketDepth > 0 || /\S/.test(text[pos]))) { if (stringChar) { if (text[pos] == stringChar) { stringChar = ""; ++pos; } else if (text[pos] == "\\") { pos += 2; } else { ++pos; } } else { if ("\"'`".includes(text[pos])) { stringChar = text[pos]; } else if (text[pos] == "[") { ++bracketDepth; } else if (text[pos] == "]") { --bracketDepth; } ++pos; } } let nameEnd = pos; if (text[nameStart] === "[") { nameStart = skipWs(text, nameStart + 1); nameEnd = skipWith(text, nameStart, /[^\s=\]]/); } pos = skipWith(text, pos, /[\s-]/); return { name: text.substring(nameStart, nameEnd), newText: text.substring(pos) }; } function skipWs(text, pos) { return skipWith(text, pos, /\s/); } function skipWith(text, pos, reg) { while (pos < text.length && reg.test(text[pos])) { ++pos; } return pos; } // src/lib/converter/comments/textParser.ts import { HtmlAttributeParser, ParserState } from "#node-utils"; import { i18n as i18n2 } from "#utils"; import MarkdownIt from "markdown-it"; var MdHelpers = new MarkdownIt().helpers; var TextParserReentryState = class { withinLinkLabel = false; withinLinkDest = false; lastPartWasNewline = false; checkState(token) { switch (token.kind) { case "code" /* Code */: if (/\n\s*\n/.test(token.text)) { this.withinLinkLabel = false; this.withinLinkDest = false; } break; case "new_line" /* NewLine */: if (this.lastPartWasNewline) { this.withinLinkLabel = false; this.withinLinkDest = false; } break; } this.lastPartWasNewline = token.kind === "new_line" /* NewLine */; } }; function textContent(parserData, outContent, reentry) { let lastPartEnd = 0; let canEndMarkdownLink = true; const data = { ...parserData, pos: 0 // relative to the token }; function addRef(ref) { canEndMarkdownLink = true; outContent.push({ kind: "text", text: data.token.text.slice(lastPartEnd, ref.pos) }); const link = { kind: "relative-link", text: data.token.text.slice(ref.pos, ref.end), target: ref.target, targetAnchor: ref.targetAnchor }; outContent.push(link); lastPartEnd = ref.end; data.pos = ref.end; if (!ref.target && data.validationOptions.invalidPath) { data.validationWarning( i18n2.relative_path_0_is_not_a_file_and_will_not_be_copied_to_output( data.token.text.slice(ref.pos, ref.end) ), { kind: "text" /* Text */, // ref.pos is relative to the token, but this pos is relative to the file. pos: data.token.pos + ref.pos, text: data.token.text.slice(ref.pos, ref.end) } ); } } while (data.pos < data.token.text.length) { if (canEndMarkdownLink) { const link = checkMarkdownLink(data, reentry); if (link) { addRef(link); continue; } canEndMarkdownLink = !reentry.withinLinkLabel && !reentry.withinLinkDest; } const reference = checkReference(data); if (reference) { addRef(reference); continue; } const tagLinks = checkTagLink(data); if (tagLinks.length) { for (const tagLink of tagLinks) { addRef(tagLink); } continue; } const atNewLine = data.token.text[data.pos] === "\n"; data.atNewLine = atNewLine; if (atNewLine && !reentry.withinLinkDest) canEndMarkdownLink = true; ++data.pos; } if (lastPartEnd !== data.token.text.length) { outContent.push({ kind: "text", text: data.token.text.slice(lastPartEnd) }); } } function checkMarkdownLink(data, reentry) { const { token, sourcePath, files } = data; let searchStart; if (reentry.withinLinkLabel || reentry.withinLinkDest) { searchStart = data.pos; } else if (token.text[data.pos] === "[") { searchStart = data.pos + 1; } else { return; } if (!reentry.withinLinkDest) { const labelEnd = findLabelEnd(token.text, searchStart); if (labelEnd === -1 || token.text[labelEnd] === "\n") { reentry.withinLinkLabel = labelEnd !== data.pos || !data.atNewLine; return; } reentry.withinLinkLabel = false; if (!token.text.startsWith("](", labelEnd)) return; searchStart = labelEnd + 2; } const end = token.text.length; let lookahead = searchStart; for (let newlines = 0; ; ++lookahead) { if (lookahead === end) { reentry.withinLinkDest = true; return; } switch (token.text[lookahead]) { case "\n": if (++newlines === 2) { reentry.withinLinkDest = false; return; } continue; case " ": case " ": continue; } break; } reentry.withinLinkDest = false; const link = MdHelpers.parseLinkDestination(token.text, lookahead, end); if (link.ok) { const decoded = decodeURI(link.str); if (isRelativePath(decoded)) { const { target, anchor } = files.register( sourcePath, decoded ) || { target: void 0, anchor: void 0 }; return { pos: lookahead, end: link.pos, target, targetAnchor: anchor }; } data.pos = link.pos - 1; } } function checkReference(data) { const { atNewLine, pos, token, files, sourcePath } = data; if (atNewLine) { let lookahead = pos; while (/[ \t]/.test(token.text[lookahead])) { ++lookahead; } if (token.text[lookahead] === "[" && token.text[lookahead + 1] !== "^") { while (lookahead < token.text.length && /[^\n\]]/.test(token.text[lookahead])) { ++lookahead; } if (token.text.startsWith("]:", lookahead)) { lookahead += 2; while (/[ \t]/.test(token.text[lookahead])) { ++lookahead; } const link = MdHelpers.parseLinkDestination( token.text, lookahead, token.text.length ); if (link.ok) { const decoded = decodeURI(link.str); if (isRelativePath(decoded)) { const { target, anchor } = files.register( sourcePath, decoded ) || { target: void 0, anchor: void 0 }; return { pos: lookahead, end: link.pos, target, targetAnchor: anchor }; } data.pos = link.pos - 1; } } } } } function checkTagLink(data) { const { pos, token } = data; if (token.text.startsWith("<img ", pos)) { data.pos += 4; return checkAttributes(data, { src: checkAttributeDirectPath, srcset: checkAttributeSrcSet }); } if (token.text.startsWith("<link ", pos)) { data.pos += 4; return checkAttributes(data, { // cspell:words imagesrcset imagesrcset: checkAttributeSrcSet }); } if (token.text.startsWith("<a ", pos)) { data.pos += 3; return checkAttributes(data, { href: checkAttributeDirectPath }); } if (token.text.startsWith("<source ", pos)) { data.pos += 8; return checkAttributes