Skip to content

review — parser

Parser

Functions

parseFeedbackFile

function parseFeedbackFile(feedbackPath: string): ReviewFeedback[]
TypeScript

Use parseFeedbackFile to turn a structured feedback markdown file into a typed array of review comments that skrypt can apply when regenerating documentation.

Reach for this when you've collected human edits or reviewer notes in a feedback file and want to feed them back into the doc generation cycle. It's the bridge between a reviewer's markdown annotations and the structured data skrypt needs to revise specific lines or sections.

The function reads the file synchronously and parses it by section — each ## heading identifies a target doc file, and each bullet beneath it becomes an individual feedback item, optionally scoped to a line number.

Parameters

NameTypeRequiredDescription
feedbackPathstringYesAbsolute or relative path to the feedback markdown file. Must follow the ## filename / - Line N: comment format — malformed sections are silently skipped.

Returns

Returns an array of ReviewFeedback objects, each containing the target file path, an optional line number, and the comment text. Pass this array to your doc regeneration step to apply targeted revisions to specific output files.

Heads up

  • The file is read synchronously — avoid calling this in a hot path or on large feedback files in a long-running server process.
  • Bullets without a Line N: prefix are treated as general (file-level) feedback, not line-specific. Both forms are valid and will appear in the returned array.

Example:

import { readFileSync, writeFileSync } from "fs";
import { tmpdir } from "os";
import { join } from "path";

interface ReviewFeedback {
  file: string;
  line?: number;
  comment: string;
}

function parseFeedbackFile(feedbackPath: string): ReviewFeedback[] {
  const content = readFileSync(feedbackPath, "utf-8");
  const lines = content.split("\n");
  const feedback: ReviewFeedback[] = [];

  let currentFile: string | null = null;

  for (const line of lines) {
    const fileMatch = line.match(/^##\s+(.+)/);
    if (fileMatch) {
      currentFile = fileMatch[1].trim();
      continue;
    }

    if (!currentFile) continue;

    const lineMatch = line.match(/^-\s+Line\s+(\d+):\s+(.+)/);
    if (lineMatch) {
      feedback.push({
        file: currentFile,
        line: parseInt(lineMatch[1], 10),
        comment: lineMatch[2].trim(),
      });
      continue;
    }

    const generalMatch = line.match(/^-\s+General:\s+(.+)/);
    if (generalMatch) {
      feedback.push({
        file: currentFile,
        comment: generalMatch[1].trim(),
      });
    }
  }

  return feedback;
}

const feedbackContent = `
## docs/api/users.md
- Line 42: Use the SDK client.get() method, not raw fetch()
- General: Too formal, make it conversational

## docs/api/auth.md
- Line 17: Missing error handling example
- Line 88: The token expiry note should come before the code block
`.trim();

const feedbackPath = join(tmpdir(), "skrypt-feedback.md");
writeFileSync(feedbackPath, feedbackContent, "utf-8");

try {
  const feedback = parseFeedbackFile(feedbackPath);
  console.log(`Parsed ${feedback.length} feedback items:\n`);
  for (const item of feedback) {
    const location = item.line ? `line ${item.line}` : "general";
    console.log(`[${item.file}] (${location}): ${item.comment}`);
  }
} catch (err) {
  console.error("Failed to parse feedback file:", err);
}

// Expected output:
// Parsed 4 feedback items:
//
// [docs/api/users.md] (line 42): Use the SDK client.get() method, not raw fetch()
// [docs/api/users.md] (general): Too formal, make it conversational
// [docs/api/auth.md] (line 17): Missing error handling example
// [docs/api/auth.md] (line 88): The token expiry note should come before the code block
TypeScript

parseInlineComments

function parseInlineComments(docsDir: string): ReviewFeedback[]
TypeScript

Use parseInlineComments to collect every FIXME and TODO annotation left inside your generated markdown files so you can surface doc debt before publishing.

After running skrypt generate, writers and reviewers often leave <!-- FIXME: ... --> or <!-- TODO: ... --> comments directly in the MDX output to flag incomplete sections, missing examples, or inaccurate descriptions. Call this function to sweep the entire docs directory and pull those comments into a structured list you can act on — fail a CI check, print a report, or pipe into an issue tracker.

It recursively walks docsDir, reads every .md / .mdx file, and extracts any HTML comment matching <!-- FIXME: ... --> or <!-- TODO: ... -->. Each match becomes a ReviewFeedback entry with the file path, line number, and comment text.

Parameters

NameTypeRequiredDescription
docsDirstringYesPath to the directory containing your generated markdown files — typically the -o output path you passed to skrypt generate. Scanned recursively.

Returns

An array of ReviewFeedback objects, one per comment found. Each item tells you which file and line the comment lives on and what it says. Pass the array to a reporter, throw if its length is non-zero in CI, or filter by type ("FIXME" vs "TODO") to triage urgency.

Returns an empty array when no comments are found — safe to call unconditionally.

Heads up

  • Only HTML comment syntax is matched (<!-- FIXME: ... -->). Plain-text TODO: or JSDoc-style // TODO inside code fences are ignored.
  • The scan is synchronous and reads every file in the tree — avoid pointing it at a directory that contains large non-doc assets.

Example:

import { readdirSync, readFileSync, statSync, mkdirSync, writeFileSync } from "fs";
import { join, extname } from "path";
import { tmpdir } from "os";

// --- Inline types (no library import) ---
interface ReviewFeedback {
  file: string;
  line: number;
  type: "FIXME" | "TODO";
  message: string;
}

// --- Inline implementation (mirrors the real function) ---
function parseInlineComments(docsDir: string): ReviewFeedback[] {
  const feedback: ReviewFeedback[] = [];
  const PATTERN = /<!--\s*(FIXME|TODO):\s*(.*?)\s*-->/g;

  function walk(dir: string): void {
    const entries = readdirSync(dir);
    for (const entry of entries) {
      const fullPath = join(dir, entry);
      const stat = statSync(fullPath);
      if (stat.isDirectory()) {
        walk(fullPath);
      } else if ([".md", ".mdx"].includes(extname(entry))) {
        const content = readFileSync(fullPath, "utf-8");
        const lines = content.split("\n");
        lines.forEach((line, idx) => {
          let match: RegExpExecArray | null;
          PATTERN.lastIndex = 0;
          while ((match = PATTERN.exec(line)) !== null) {
            feedback.push({
              file: fullPath,
              line: idx + 1,
              type: match[1] as "FIXME" | "TODO",
              message: match[2],
            });
          }
        });
      }
    }
  }

  walk(docsDir);
  return feedback;
}

// --- Demo: create a temp docs directory with realistic content ---
const docsDir = join(tmpdir(), "skrypt-demo-docs");
mkdirSync(join(docsDir, "api"), { recursive: true });

writeFileSync(
  join(docsDir, "api", "authentication.mdx"),
  `# Authentication\n\n<!-- FIXME: Add OAuth2 flow diagram before launch -->\n\nUse API keys to authenticate requests.\n\n<!-- TODO: Link to the key rotation guide -->\n`
);
writeFileSync(
  join(docsDir, "api", "webhooks.mdx"),
  `# Webhooks\n\nRegister an endpoint to receive real-time events.\n`
);

// --- Run and report ---
try {
  const issues = parseInlineComments(docsDir);

  if (issues.length === 0) {
    console.log("✅ No inline comments found — docs are clean.");
  } else {
    console.log(`⚠️  Found ${issues.length} inline comment(s):\n`);
    for (const issue of issues) {
      const shortPath = issue.file.replace(docsDir + "/", "");
      console.log(`  [${issue.type}] ${shortPath}:${issue.line} — ${issue.message}`);
    }

    // Fail CI if any FIXMEs remain
    const blockers = issues.filter((i) => i.type === "FIXME");
    if (blockers.length > 0) {
      console.error(`\n❌ ${blockers.length} FIXME(s) must be resolved before publishing.`);
      process.exit(1);
    }
  }
} catch (err) {
  console.error("Failed to scan docs directory:", err);
  process.exit(1);
}

// Expected output:
// ⚠️  Found 2 inline comment(s):
//
//   [FIXME] api/authentication.mdx:3 — Add OAuth2 flow diagram before launch
//   [TODO]  api/authentication.mdx:7 — Link to the key rotation guide
//
// ❌ 1 FIXME(s) must be resolved before publishing.
TypeScript
Was this helpful?