async function processLinkNode()

in src/server/remarkPlugins/swagger/index.ts [115:216]


async function processLinkNode(target: Target, context: Context) {
  const [node] = target;
  if (!node.url || !node.url.startsWith("swagger:")) {
    return;
  }
  const pathname = node.url.split('?')[0].slice("swagger:".length);
  const queryString = node.url.split('?')[1];
  const queryParams = queryString 
    ? new Map(queryString.split('&').map(param => {
        const [key, value = ''] = param.split('=');
        return [decodeURIComponent(key).replace(/\+/g, ' '), decodeURIComponent(value).replace(/\+/g, ' ')] as const;
      }))
    : new Map();
  const relativePath = path.relative(context.baseDir, context.filePath);

  // Split the path into segments
  const segments = pathname.split('/').filter(Boolean);
  
  // Parse the path segments
  const isAdmin = segments[0] === 'admin';
  if (!isAdmin || segments.length < 2) {
    throw new Error(`Invalid swagger URL format: ${pathname}`);
  }

  // Extract components
  let apiVersion = segments[1];
  let apiType: SwaggerApiType = 'default';
  let operationName: string;

  if (segments.length === 3) {
    operationName = segments[2];
  } else {
    apiType = segments[2] as SwaggerApiType;
    operationName = segments[3];
  }

  const position = node.position ? ` ${relativePath} at position ${node.position.start.line}:${node.position.start.column}` : '';

  // Validate the subContext is a valid SwaggerFileType
  if (apiType !== 'default' && !getSwaggerFileName(apiType)) {
    throw new Error(`Invalid swagger sub-context: ${apiType}${position}`);
  }

  const swaggerResult = context.cache.getSwaggerResult(relativePath, apiType);
  const swaggerJson = swaggerResult.json;

  // Search through all paths in the swagger JSON
  let matches: { path: string; method: string; tags: string[]; summary?: string }[] = [];
  
  const tagParam = queryParams.get('tag') || 
    (operationName.startsWith('PersistentTopics_') ? '^persistent' : undefined);
  const summaryParam = queryParams.get('summary');

  // Handle negation separately from regex pattern
  const isTagNegated = tagParam?.startsWith('!') || false;
  const isSummaryNegated = summaryParam?.startsWith('!') || false;

  // Convert params to regex patterns without negation
  const tagRegex = tagParam && new RegExp(isTagNegated ? tagParam.slice(1) : tagParam);
  const summaryRegex = summaryParam && new RegExp(isSummaryNegated ? summaryParam.slice(1) : summaryParam);

  for (const [path, methods] of Object.entries(swaggerJson.paths || {})) {
    for (const [method, operation] of Object.entries(methods as Record<string, {
      operationId: string;
      tags: string[];
      summary?: string;
    }>)) {
      const tagMatches = !tagRegex || (operation.tags &&operation.tags.some(t => tagRegex.test(t)) !== isTagNegated);
      const summaryMatches = !summaryRegex || (operation.summary && summaryRegex.test(operation.summary) !== isSummaryNegated);
      if (operation.operationId === operationName && tagMatches && summaryMatches) {
        matches.push({ path, method, tags: operation.tags, summary: operation.summary });
      }
    }
  }

  if (matches.length === 0) {
    throw new Error(`Operation ${operationName} tag:${tagParam} summary:${summaryParam} not found in swagger JSON${position}`);
  }

  if (matches.length > 1) {
    console.warn(`Multiple operations found for ${operationName} tag:${tagParam} summary:${summaryParam}:\n${matches.map(m => `${m.method} ${m.path} tags:${m.tags.join(',')} summary:${m.summary}`).join('\n')}${position}`);
  }

  // Select the match with the longest path since there are duplicates
  const longestMatch = matches.reduce((longest, current) => 
    current.path.length > longest.path.length ? current : longest
  );

  const foundPath = swaggerJson.basePath + longestMatch.path;
  const foundMethod = longestMatch.method.toUpperCase();

  const restApiBaseUrl = context.restApiBaseUrlMapping[apiType];

  const swaggerVersion = swaggerResult.version;

  node.url = `${restApiBaseUrl}?version=${swaggerVersion}&apiVersion=${apiVersion}#operation/${operationName}`;

  node.children = [{
    type: "text",
    value: `${foundMethod} ${foundPath}`
  }];
}