Skip to content

Topic Organizer

Organizer

Functions

organizeByTopic

function organizeByTopic(docs: GeneratedDoc[], config: TopicConfig = DEFAULT_TOPIC_CONFIG): Topic[]
TypeScript

Use organizeByTopic to group flat documentation output into logical topic clusters — turning a list of generated API docs into a structured hierarchy ready for navigation menus, sidebar generation, or MDX page organization.

Reach for this after running your documentation generation step, when you want to present docs by concept ("Authentication", "Webhooks", "Users") rather than by the file they came from. It's the bridge between raw GeneratedDoc[] output and a publishable doc site structure.

The function maps each GeneratedDoc to a topic bucket based on the rules in config. Docs that don't match any explicit topic rule fall into a default catch-all topic. The result preserves the original doc content while adding the grouping layer on top.

Parameters

NameTypeRequiredDescription
docsGeneratedDoc[]YesThe flat array of generated docs returned by your generation step — pass the full output before filtering or sorting.
configTopicConfigNoRules that control how docs are assigned to topics. Defaults to DEFAULT_TOPIC_CONFIG, which groups by filename prefix and common naming conventions. Provide your own to enforce project-specific groupings.

Returns

Returns a Topic[] array where each Topic contains a label, slug, and the GeneratedDoc[] entries assigned to it. Pass this directly to your MDX renderer or sidebar builder — each Topic maps naturally to a doc section or nav group.

Heads up

  • Docs are assigned to the first matching topic rule in config, so order your topic matchers from most specific to most general to avoid broad rules swallowing entries meant for narrower topics.
  • If docs is empty, the function returns an empty array rather than a list of empty topic shells — check for this before rendering navigation.

Example:

type GeneratedDoc = {
  name: string;
  description: string;
  filePath: string;
  signature: string;
  parameters: { name: string; type: string; description: string }[];
  returns: string;
};

type Topic = {
  label: string;
  slug: string;
  docs: GeneratedDoc[];
};

type TopicRule = {
  label: string;
  slug: string;
  match: (doc: GeneratedDoc) => boolean;
};

type TopicConfig = {
  rules: TopicRule[];
  defaultTopic: { label: string; slug: string };
};

const DEFAULT_TOPIC_CONFIG: TopicConfig = {
  rules: [
    {
      label: "Authentication",
      slug: "authentication",
      match: (doc) => doc.filePath.includes("auth"),
    },
    {
      label: "Users",
      slug: "users",
      match: (doc) => doc.filePath.includes("user"),
    },
    {
      label: "Payments",
      slug: "payments",
      match: (doc) => doc.filePath.includes("payment") || doc.filePath.includes("billing"),
    },
  ],
  defaultTopic: { label: "General", slug: "general" },
};

function organizeByTopic(
  docs: GeneratedDoc[],
  config: TopicConfig = DEFAULT_TOPIC_CONFIG
): Topic[] {
  const topicDocs = new Map<string, GeneratedDoc[]>();

  for (const rule of config.rules) {
    topicDocs.set(rule.slug, []);
  }
  topicDocs.set(config.defaultTopic.slug, []);

  for (const doc of docs) {
    const matchedRule = config.rules.find((rule) => rule.match(doc));
    const slug = matchedRule ? matchedRule.slug : config.defaultTopic.slug;
    topicDocs.get(slug)!.push(doc);
  }

  const allRules = [
    ...config.rules,
    config.defaultTopic,
  ];

  return allRules
    .map((rule) => ({
      label: rule.label,
      slug: rule.slug,
      docs: topicDocs.get(rule.slug) ?? [],
    }))
    .filter((topic) => topic.docs.length > 0);
}

// --- Example usage ---

const generatedDocs: GeneratedDoc[] = [
  {
    name: "createUser",
    description: "Creates a new user account.",
    filePath: "src/user/create.ts",
    signature: "function createUser(email: string, password: string): Promise<User>",
    parameters: [
      { name: "email", type: "string", description: "User's email address." },
      { name: "password", type: "string", description: "Plaintext password — hashed before storage." },
    ],
    returns: "Promise<User>",
  },
  {
    name: "generateToken",
    description: "Issues a signed JWT for an authenticated session.",
    filePath: "src/auth/tokens.ts",
    signature: "function generateToken(userId: string): string",
    parameters: [
      { name: "userId", type: "string", description: "The ID of the authenticated user." },
    ],
    returns: "string",
  },
  {
    name: "createCheckoutSession",
    description: "Starts a Stripe checkout session for a given plan.",
    filePath: "src/billing/checkout.ts",
    signature: "function createCheckoutSession(planId: string): Promise<Session>",
    parameters: [
      { name: "planId", type: "string", description: "The billing plan identifier." },
    ],
    returns: "Promise<Session>",
  },
  {
    name: "healthCheck",
    description: "Returns server health status.",
    filePath: "src/utils/health.ts",
    signature: "function healthCheck(): { status: string }",
    parameters: [],
    returns: "{ status: string }",
  },
];

try {
  const topics = organizeByTopic(generatedDocs);

  for (const topic of topics) {
    console.log(`\n[${topic.label}] (/${topic.slug})`);
    for (const doc of topic.docs) {
      console.log(`  - ${doc.name}: ${doc.description}`);
    }
  }
} catch (err) {
  console.error("Failed to organize docs:", err);
}

// Expected output:
// [Authentication] (/authentication)
//   - generateToken: Issues a signed JWT for an authenticated session.
//
// [Users] (/users)
//   - createUser: Creates a new user account.
//
// [Payments] (/payments)
//   - createCheckoutSession: Starts a Stripe checkout session for a given plan.
//
// [General] (/general)
//   - healthCheck: Returns server health status.
TypeScript

detectCrossReferences

function detectCrossReferences(docs: GeneratedDoc[]): CrossReference[]
TypeScript

Use detectCrossReferences to automatically discover which documented elements reference each other, so you can render "See also" links, build a knowledge graph, or validate that your API surface is internally consistent.

Call this after you've generated your full set of docs — typically as a post-processing step before writing MDX output. It's especially useful when you want to surface relationships between types, functions, and classes that share names in their descriptions or signatures.

The function scans every GeneratedDoc entry and checks each element's description and signature against the names of all other documented elements. When it finds a match, it records a directional reference between the two.

Parameters

NameTypeRequiredDescription
docsGeneratedDoc[]YesThe full array of generated documentation objects to analyze — pass the complete set so cross-references aren't missed due to partial input.

Returns

Returns an array of CrossReference objects, each describing a directional link between two documented elements. Use the results to inject "Related" sections into your MDX pages, or pass them to a graph renderer to visualize your API's dependency structure. An empty array means no relationships were detected.

Heads up

  • Pass the complete docs array. Running this on a subset will miss references to elements outside that subset, producing an incomplete reference graph.
  • Matching is name-based, so generic names like options or config may produce false positives if multiple elements share those names.

Example:

type ElementKind = "function" | "class" | "type" | "interface" | "constant";

interface CodeElement {
  name: string;
  kind: ElementKind;
  signature: string;
  description?: string;
  filePath: string;
}

interface GeneratedDoc {
  element: CodeElement;
  description: string;
  params?: { name: string; type: string; description: string }[];
  returns?: string;
  example?: string;
}

interface CrossReference {
  fromElement: string;
  toElement: string;
  context: string;
}

function detectCrossReferences(docs: GeneratedDoc[]): CrossReference[] {
  const refs: CrossReference[] = [];
  const elementNames = new Set(docs.map((d) => d.element.name));

  for (const doc of docs) {
    const { element } = doc;
    const textToSearch = `${doc.description} ${element.signature}`;

    for (const name of elementNames) {
      if (name === element.name) continue;

      const pattern = new RegExp(`\\b${name}\\b`);
      if (pattern.test(textToSearch)) {
        refs.push({
          fromElement: element.name,
          toElement: name,
          context: doc.description,
        });
      }
    }
  }

  return refs;
}

const docs: GeneratedDoc[] = [
  {
    element: {
      name: "createUser",
      kind: "function",
      signature: "function createUser(options: UserOptions): Promise<User>",
      filePath: "src/users.ts",
    },
    description:
      "Creates a new user. Accepts a UserOptions object and returns a User on success.",
  },
  {
    element: {
      name: "UserOptions",
      kind: "interface",
      signature: "interface UserOptions { email: string; role: Role }",
      filePath: "src/types.ts",
    },
    description:
      "Configuration passed to createUser. The role field must be a valid Role value.",
  },
  {
    element: {
      name: "Role",
      kind: "type",
      signature: "type Role = 'admin' | 'editor' | 'viewer'",
      filePath: "src/types.ts",
    },
    description: "Union type representing permission levels across the system.",
  },
  {
    element: {
      name: "User",
      kind: "interface",
      signature: "interface User { id: string; email: string; role: Role }",
      filePath: "src/types.ts",
    },
    description: "Represents an authenticated user returned by createUser.",
  },
];

try {
  const crossRefs = detectCrossReferences(docs);

  console.log(`Found ${crossRefs.length} cross-references:\n`);
  for (const ref of crossRefs) {
    console.log(`  ${ref.fromElement} → ${ref.toElement}`);
    console.log(`  context: "${ref.context.slice(0, 72)}..."\n`);
  }
} catch (err) {
  console.error("Failed to detect cross-references:", err);
}

// Expected output:
// Found 4 cross-references:
//
//   createUser → UserOptions
//   context: "Creates a new user. Accepts a UserOptions object and returns a User..."
//
//   createUser → User
//   context: "Creates a new user. Accepts a UserOptions object and returns a User..."
//
//   UserOptions → createUser
//   context: "Configuration passed to createUser. The role field must be a valid ..."
//
//   UserOptions → Role
//   context: "Configuration passed to createUser. The role field must be a valid ..."
TypeScript

getCrossRefsForElement

function getCrossRefsForElement(elementName: string, allRefs: CrossReference[]): CrossReference[]
TypeScript

Use getCrossRefsForElement to retrieve all cross-references that originate from a specific API element, so you can trace how one function, class, or type links to others in your documentation graph.

Reach for this when building a "Related" or "See also" section for a doc page — after skrypt scans your codebase and produces a flat list of cross-references, this filters it down to only the ones relevant to the element you're currently rendering.

It filters the full allRefs array to entries where fromElement matches elementName exactly, so the result represents outbound links from that element.

Parameters

NameTypeRequiredDescription
elementNamestringYesThe exact name of the element whose outbound references you want — must match the fromElement field in your CrossReference records
allRefsCrossReference[]YesThe complete cross-reference list produced by skrypt's scan, typically shared across all elements in a single documentation pass

Returns

A filtered CrossReference[] containing only references that originate from elementName. Pass the result to your MDX template or doc renderer to populate "See also" links. Returns an empty array if the element has no outbound references.

Heads up

  • Matching is case-sensitive and exact — "createUser" won't match "CreateUser" or "createuser".
  • This only returns outbound references (where fromElement === elementName). To find which elements point to a given element, filter on toElement instead.

Example:

type CrossReference = {
  fromElement: string;
  toElement: string;
  description: string;
};

function getCrossRefsForElement(
  elementName: string,
  allRefs: CrossReference[]
): CrossReference[] {
  return allRefs.filter((r) => r.fromElement === elementName);
}

const allRefs: CrossReference[] = [
  { fromElement: "createPayment", toElement: "validateCard", description: "Validates card before charging" },
  { fromElement: "createPayment", toElement: "sendReceipt", description: "Sends confirmation email on success" },
  { fromElement: "refundPayment", toElement: "createPayment", description: "Requires an existing payment to refund" },
  { fromElement: "validateCard", toElement: "CardSchema", description: "Uses CardSchema for field validation" },
];

try {
  const refs = getCrossRefsForElement("createPayment", allRefs);

  console.log(`Cross-references for "createPayment":`);
  refs.forEach((ref) => {
    console.log(`  → ${ref.toElement}: ${ref.description}`);
  });

  // Expected output:
  // Cross-references for "createPayment":
  //   → validateCard: Validates card before charging
  //   → sendReceipt: Sends confirmation email on success
} catch (err) {
  console.error("Failed to retrieve cross-references:", err);
}
TypeScript
Was this helpful?