UNPKG

@supernovaio/cli

Version:

Supernova.io Command Line Interface

164 lines (162 loc) 6.73 kB
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0ce14f9a-c743-50b2-959b-559515b5fa6c")}catch(e){}}(); import { readFile } from "node:fs/promises"; import path from "node:path"; import { collectFiles, toRelative } from "./helpers.js"; const docsPattern = "**/*.{md,mdx}"; const snippetWindowBefore = 3; const snippetWindowAfter = 8; const maxReferenceSnippetsPerFile = 3; function extractTitle(content) { const headingMatch = content.match(/^#\s+(.+)$/m); if (headingMatch?.[1]) { return headingMatch[1].trim(); } const metaMatch = content.match(/<Meta\s+title=["']([^"']+)["']/); return metaMatch?.[1]?.trim(); } function escapeRegex(input) { return input.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`); } function toPosix(input) { return input.split(path.sep).join("/"); } function lineAt(content, index) { return content.slice(0, index).split("\n").length; } function normalizeLine(input) { return input.replaceAll(/\s+/g, " ").trim().toLowerCase(); } function createSnippet(content, line) { const lines = content.split("\n"); const lineIndex = Math.max(0, Math.min(lines.length - 1, line - 1)); let lineStart = Math.max(1, line - snippetWindowBefore); let lineEnd = Math.min(lines.length, line + snippetWindowAfter); for (let i = lineIndex; i >= lineStart - 1; i -= 1) { if (lines[i]?.trim() === "") { lineStart = i + 2; break; } } for (let i = lineIndex; i <= lineEnd - 1; i += 1) { if (lines[i]?.trim() === "") { lineEnd = i; break; } } if (lineStart > lineEnd) { lineStart = line; lineEnd = line; } const snippet = lines.slice(lineStart - 1, lineEnd).join("\n").trimEnd(); return { lineEnd, lineStart, snippet }; } function classifyDocRecord(relativePath, component, content) { const normalizedPath = toPosix(relativePath).toLowerCase(); const componentPath = toPosix(component.componentPath).toLowerCase(); const componentDir = path.posix.dirname(componentPath); const fileName = path.posix.basename(normalizedPath); const exportName = component.exportName.toLowerCase(); const title = extractTitle(content); const normalizedTitle = title ? normalizeLine(title) : ""; const componentToken = normalizeLine(component.exportName); const inComponentDir = normalizedPath.startsWith(`${componentDir}/`); const isReadme = fileName === "readme.md" || fileName === "readme.mdx"; const isDocsLike = fileName.startsWith("docs.") || fileName.endsWith(".docs.md") || fileName.endsWith(".docs.mdx"); const fileContainsName = fileName.includes(exportName); const titleContainsName = normalizedTitle.includes(componentToken); if (inComponentDir && isReadme) { return { kind: "doc", score: 100 }; } if (inComponentDir && isDocsLike) { return { kind: "doc", score: 95 }; } if (inComponentDir) { return { kind: "doc", score: 90 }; } if (fileContainsName || titleContainsName) { return { kind: "doc", score: 80 }; } if (normalizedPath.includes("/docs/") || normalizedPath.includes("/documentation/")) { return { kind: "reference", score: 40 }; } if (normalizedPath.includes("/storybook/") || normalizedPath.includes(".stories.")) { return { kind: "reference", score: 35 }; } return { kind: "reference", score: 20 }; } export async function analyzeStorybookDocs(projectRoot, components, excludePaths = []) { const files = await collectFiles({ excludePaths, pattern: docsPattern, projectRoot, }); const records = {}; for (const file of files) { const content = await readFile(file, "utf8"); const title = extractTitle(content); const relativePath = toRelative(projectRoot, file); for (const component of components) { const classification = classifyDocRecord(relativePath, component, content); const componentPattern = new RegExp(`\\b${escapeRegex(component.exportName)}\\b`, "gm"); const matches = [...content.matchAll(componentPattern)]; const isFullDoc = classification.kind === "doc"; if (!isFullDoc && matches.length === 0) { continue; } if (!records[component.componentKey]) { records[component.componentKey] = []; } if (isFullDoc) { const firstMatchIndex = typeof matches[0]?.index === "number" ? matches[0].index : 0; const line = lineAt(content, firstMatchIndex); records[component.componentKey].push({ content, docPath: relativePath, kind: "doc", lineEnd: line, lineStart: line, score: classification.score, title, }); continue; } const snippetMatches = matches.slice(0, maxReferenceSnippetsPerFile); for (const match of snippetMatches) { if (typeof match.index !== "number") { continue; } const matchLine = lineAt(content, match.index); const snippet = createSnippet(content, matchLine); records[component.componentKey].push({ docPath: relativePath, kind: "reference", lineEnd: snippet.lineEnd, lineStart: snippet.lineStart, score: classification.score, snippet: snippet.snippet, title, }); } } } for (const componentKey of Object.keys(records)) { records[componentKey] = records[componentKey].sort((a, b) => { if (a.score !== b.score) { return b.score - a.score; } if (a.kind !== b.kind) { return a.kind === "doc" ? -1 : 1; } if (a.docPath === b.docPath) { if (a.lineStart === b.lineStart) { return a.lineEnd - b.lineEnd; } return a.lineStart - b.lineStart; } return a.docPath.localeCompare(b.docPath); }); } return records; } //# sourceMappingURL=storybook-docs.js.map //# debugId=0ce14f9a-c743-50b2-959b-559515b5fa6c