diff --git a/docusaurus.config.ts b/docusaurus.config.ts index a84771a782..fd73fcc50b 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -89,6 +89,46 @@ const config: Config = { } }; }, + [ + require.resolve('./plugins/llms-symlinks'), + { + docsDir: 'docs', + + // Use site metadata in generated headers + title: 'Oasis Documentation', + description: 'Official Oasis developer documentation.', + + // Generate both files + generateLLMsTxt: true, + generateLLMsFullTxt: true, + + // Clean MDX a bit but keep content readable + excludeImports: true, + removeDuplicateHeadings: true, + + // Strip "docs" from URLs to match the deployed site paths + pathTransformation: { + ignorePaths: ['docs'], + }, + + // Prioritize Build docs in order: use-cases, ROFL, Sapphire, CLI, then rest of build. + // Keep unmatched docs at the end so we don't drop anything. + includeOrder: [ + 'docs/build/use-cases/**', + 'docs/build/rofl/**', + 'docs/build/sapphire/**', + 'docs/build/tools/cli/**', + 'docs/build/**', + ], + includeUnmatchedLast: true, + // Mirror ADR exclusions from the main docs plugin to avoid boilerplate entries. + ignoreFiles: [ + 'docs/adrs/README.md', + 'docs/adrs/0000-architectural-decision-records.md', + 'docs/adrs/template.md', + ], + }, + ], [ '@docusaurus/plugin-client-redirects', redirectsOptions, diff --git a/package.json b/package.json index e1dce5154e..1c6561f0da 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "@docusaurus/types": "3.9.2", "@types/plotly.js-basic-dist": "^1.54.4", "@types/react-plotly.js": "^2.6.3", + "docusaurus-plugin-llms": "0.2.2", "jest": "^29.7.0", "ts-jest": "^29.1.1", "ts-node": "^10.9.2", diff --git a/plugins/llms-symlinks.js b/plugins/llms-symlinks.js new file mode 100644 index 0000000000..3c40ef7e2b --- /dev/null +++ b/plugins/llms-symlinks.js @@ -0,0 +1,593 @@ +/** + * Docusaurus LLMs plugin wrapper that adds symlink support and better content cleaning. + * + * This wraps the published docusaurus-plugin-llms to: + * - Follow symlinks when discovering markdown files (for external doc repos) + * - Clean MDX/JSX components from generated output + * - Inline code snippets referenced via ![code](...) syntax + * - Extract better descriptions for the TOC + */ + +const path = require('path'); +const fs = require('fs/promises'); +const fsSync = require('fs'); + +const llmsPlugin = require('docusaurus-plugin-llms').default; +const llmsUtils = require('docusaurus-plugin-llms/lib/utils'); +const llmsProcessor = require('docusaurus-plugin-llms/lib/processor'); +const llmsGenerator = require('docusaurus-plugin-llms/lib/generator'); + +// Global State + +let currentMarkdownFilePath = null; +const externalBaseDir = path.resolve(process.cwd(), 'external'); +let externalRoots = null; + +// Pre-compiled Patterns + +const PATTERNS = { + // Content cleaning + codeFence: /^```/, + importStatement: /^\s*import\s+.+$/, + mdxOpenTag: /^<([A-Z][A-Za-z0-9]*)\b/, + mdxCloseTag: /^\s*<\/[A-Z][A-Za-z0-9]*>\s*$/, + admonition: /^(\s*):::[\w-]*(?:\[.*?\])?(?:\s+(.*))?$/, + htmlTags: + /<\/?(?:div|span|p|br|hr|img|a|strong|em|b|i|u|h[1-6]|ul|ol|li|table|tr|td|th|thead|tbody|iframe|details|summary)\b[^>]*>/gi, + anchorId: /\s*\{#[\w-]+\}/g, + imageLink: /!\[([^\]]*)\]\([^)]+\)/g, + heading: /^\s*(#+)\s+(.+)$/, + multipleNewlines: /\n{3,}/g, + + // Description extraction + headingLine: /^#+\s+/, + jsxTag: /^<[A-Za-z/]/, + admonitionStart: /^:::/, + refLink: /^\[.*\]:/, + horizontalRule: /^[-*_]{3,}\s*$/, + listItem: /^[-*+]\s+/, + orderedListItem: /^\d+\.\s+/, + imagePlaceholder: /^\[Image:/, + + // Markdown cleanup + mdLink: /\[([^\]]+)\]\([^)]+\)/g, + refStyleLink: /\[([^\]]+)\]\[[^\]]*\]/g, + bold: /\*\*([^*]+)\*\*/g, + italicAsterisk: /(?]*\/>/g, +}; + +// Helper Functions + +function getExternalRoots() { + if (externalRoots !== null) return externalRoots; + try { + externalRoots = fsSync + .readdirSync(externalBaseDir, {withFileTypes: true}) + .filter((entry) => entry.isDirectory()) + .map((entry) => path.join(externalBaseDir, entry.name)); + } catch { + externalRoots = []; + } + return externalRoots; +} + +function resolveSnippetPath(relPath, baseDir) { + const primary = path.resolve(baseDir, relPath); + if (fsSync.existsSync(primary)) return primary; + + const normalized = path.normalize(relPath).replace(/^(\.\.[/\\])+/, ''); + if (normalized && normalized !== relPath) { + for (const root of getExternalRoots()) { + const candidate = path.join(root, normalized); + if (fsSync.existsSync(candidate)) { + return candidate; + } + } + } + return null; +} + +function shouldSkipLine(trimmed, inParagraph) { + // Returns: true to skip entirely, 'break' to end paragraph, false to include + if (PATTERNS.headingLine.test(trimmed)) return inParagraph ? 'break' : true; + if (/^import\s+/.test(trimmed)) return true; + if (PATTERNS.jsxTag.test(trimmed)) return inParagraph ? 'break' : true; + if (PATTERNS.admonitionStart.test(trimmed)) + return inParagraph ? 'break' : true; + if (PATTERNS.refLink.test(trimmed)) return true; + if (PATTERNS.horizontalRule.test(trimmed)) + return inParagraph ? 'break' : true; + if (PATTERNS.imagePlaceholder.test(trimmed)) return true; + if ( + PATTERNS.listItem.test(trimmed) || + PATTERNS.orderedListItem.test(trimmed) + ) { + return inParagraph ? 'break' : true; + } + return false; +} + +function cleanMarkdownSyntax(text) { + return text + .replace(PATTERNS.mdLink, '$1') + .replace(PATTERNS.refStyleLink, '$1') + .replace(PATTERNS.bold, '$1') + .replace(PATTERNS.italicAsterisk, '$1') + .replace(PATTERNS.italicUnderscore, '$1') + .replace(PATTERNS.inlineCode, '$1') + .replace(PATTERNS.imagePlaceholderGlobal, '') + .trim(); +} + +function truncateDescription(description, maxLength = 200) { + if (description.length <= maxLength) return description; + + const truncated = description.substring(0, maxLength); + const lastPeriod = truncated.lastIndexOf('. '); + const lastExclaim = truncated.lastIndexOf('! '); + const lastQuestion = truncated.lastIndexOf('? '); + const lastSentenceEnd = Math.max(lastPeriod, lastExclaim, lastQuestion); + + if (lastSentenceEnd > maxLength * 0.4) { + return description.substring(0, lastSentenceEnd + 1); + } + + const lastSpace = truncated.lastIndexOf(' '); + if (lastSpace > maxLength * 0.5) { + return truncated.substring(0, lastSpace) + '...'; + } + return truncated + '...'; +} + +// Description Extraction + +function deriveDescription(doc) { + if (doc?.frontMatter?.description) { + return String(doc.frontMatter.description).trim(); + } + + const source = doc?.content || doc?.description || ''; + const lines = source.split('\n'); + const paragraphLines = []; + let inParagraph = false; + let inCodeBlock = false; + + for (const line of lines) { + const trimmed = line.trim(); + + if (PATTERNS.codeFence.test(trimmed)) { + inCodeBlock = !inCodeBlock; + continue; + } + if (inCodeBlock) continue; + + if (!trimmed) { + if (inParagraph && paragraphLines.length > 0) break; + continue; + } + + const skipResult = shouldSkipLine(trimmed, inParagraph); + if (skipResult === 'break') break; + if (skipResult === true) continue; + + inParagraph = true; + paragraphLines.push(trimmed); + + if (paragraphLines.join(' ').length > 400) break; + } + + if (paragraphLines.length === 0) return ''; + + let description = paragraphLines.join(' ').replace(/\s+/g, ' ').trim(); + description = cleanMarkdownSyntax(description); + + return description || ''; +} + +// Code Snippet Inlining + +function inlineCodeSnippets(content, filePath) { + if (!filePath) return content; + + const baseDir = path.dirname(filePath); + + return content.replace(PATTERNS.codeImage, (match, alt, url) => { + if (!alt?.trim().startsWith('code')) return match; + + let urlPart = url.trim().replace(/\s*\n\s*/g, ' '); + let title = ''; + const titleMatch = urlPart.match(PATTERNS.titleInUrl); + if (titleMatch) { + urlPart = titleMatch[1]; + title = titleMatch[2]; + } + + let relPath = urlPart; + let fragment = ''; + const hashIndex = urlPart.indexOf('#'); + if (hashIndex !== -1) { + relPath = urlPart.slice(0, hashIndex); + fragment = urlPart.slice(hashIndex + 1); + } + + const resolvedPath = resolveSnippetPath(relPath, baseDir); + if (!resolvedPath) { + console.warn( + `llms-symlinks: snippet "${relPath}" not found (from ${filePath})`, + ); + return match; + } + + let snippet = fsSync + .readFileSync(resolvedPath, 'utf8') + .replace(/\r\n/g, '\n') + .replace(/\n$/, ''); + + if (fragment) { + const lines = snippet.split('\n'); + const lineRange = fragment.match(PATTERNS.lineRange); + + if (lineRange) { + const start = parseInt(lineRange[1], 10); + const end = parseInt(lineRange[2] || lineRange[1], 10); + if (start < 1 || end > lines.length || start > end) { + console.warn( + `llms-symlinks: line range ${fragment} out of bounds in ${resolvedPath}`, + ); + return match; + } + snippet = lines.slice(start - 1, end).join('\n'); + } else { + const regionStart = lines.findIndex((l) => + l.trimEnd().endsWith(`#region ${fragment}`), + ); + const regionEnd = lines.findIndex((l) => + l.trimEnd().endsWith(`#endregion ${fragment}`), + ); + if ( + regionStart === -1 || + regionEnd === -1 || + regionStart >= regionEnd + ) { + console.warn( + `llms-symlinks: region ${fragment} not found in ${resolvedPath}`, + ); + return match; + } + snippet = lines.slice(regionStart + 1, regionEnd).join('\n'); + } + } + + const tokens = alt.trim().split(/\s+/); + const lang = tokens[1] || ''; + const meta = title ? ` title="${title}"` : ''; + const info = [lang, meta.trim()].filter(Boolean).join(' ').trim(); + + return `${info ? `\`\`\`${info}` : '```'}\n${snippet}\n\`\`\``; + }); +} + +// Content Cleaning + +function cleanMarkdownContent( + content, + excludeImports = false, + removeDuplicateHeadings = false, +) { + const contentWithSnippets = inlineCodeSnippets( + content, + currentMarkdownFilePath, + ); + + const lines = contentWithSnippets.split('\n'); + const processedLines = []; + let inFence = false; + let inMdxOpeningTag = false; + + for (const line of lines) { + const trimmed = line.trim(); + + if (PATTERNS.codeFence.test(trimmed)) { + inFence = !inFence; + processedLines.push(line); + continue; + } + + if (inFence) { + processedLines.push(line); + continue; + } + + if (excludeImports && PATTERNS.importStatement.test(line)) continue; + + if (inMdxOpeningTag) { + if (trimmed.endsWith('>') || trimmed.endsWith('/>')) + inMdxOpeningTag = false; + continue; + } + + const mdxOpenMatch = trimmed.match(PATTERNS.mdxOpenTag); + if (mdxOpenMatch) { + if (!trimmed.endsWith('/>') && !trimmed.endsWith('>')) + inMdxOpeningTag = true; + continue; + } + + if (PATTERNS.mdxCloseTag.test(line)) continue; + + const admonitionMatch = line.match(PATTERNS.admonition); + if (admonitionMatch) { + if (admonitionMatch[2]) + processedLines.push(admonitionMatch[1] + admonitionMatch[2]); + continue; + } + + let cleaned = line + .replace(PATTERNS.htmlTags, '') + .replace(PATTERNS.anchorId, '') + .replace(PATTERNS.mdxInlineComponent, ''); + processedLines.push(cleaned); + } + + let cleaned = processedLines.join('\n'); + + if (removeDuplicateHeadings) { + const resultLines = []; + const splitLines = cleaned.split('\n'); + let i = 0; + + while (i < splitLines.length) { + const currentLine = splitLines[i]; + const headingMatch = currentLine.match(PATTERNS.heading); + + if (headingMatch) { + const headingText = headingMatch[2].trim(); + resultLines.push(currentLine); + i++; + + while (i < splitLines.length && splitLines[i].trim() === '') { + resultLines.push(splitLines[i]); + i++; + } + + if (i < splitLines.length) { + const nextLine = splitLines[i].trim(); + if (nextLine === headingText && !PATTERNS.headingLine.test(nextLine)) + i++; + } + } else { + resultLines.push(currentLine); + i++; + } + } + cleaned = resultLines.join('\n'); + } + + cleaned = cleaned.replace(PATTERNS.imageLink, (_, alt) => + alt.trim() ? `[Image: ${alt.trim()}]` : '', + ); + + return cleaned + .replace(/\r\n/g, '\n') + .replace(PATTERNS.multipleNewlines, '\n\n') + .trim(); +} + +// Symlink-Aware File Reader + +function createSymlinkAwareReader() { + const readMarkdownFiles = async ( + dir, + baseDir, + ignorePatterns = [], + visited = new Set(), + ) => { + let realPath; + try { + realPath = await fs.realpath(dir); + } catch { + return []; + } + + if (visited.has(realPath)) return []; + visited.add(realPath); + + let entries; + try { + entries = await fs.readdir(dir, {withFileTypes: true}); + } catch (error) { + console.warn(`Skipping unreadable directory "${dir}": ${error.message}`); + return []; + } + + const files = []; + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + + if (llmsUtils.shouldIgnoreFile(fullPath, baseDir, ignorePatterns)) + continue; + + let isDirectory = entry.isDirectory(); + let isFile = entry.isFile(); + + if (entry.isSymbolicLink()) { + try { + const stats = await fs.stat(fullPath); + isDirectory = stats.isDirectory(); + isFile = stats.isFile(); + } catch (error) { + console.warn( + `Skipping unreadable symlink "${fullPath}": ${error.message}`, + ); + continue; + } + } + + if (isDirectory) { + files.push( + ...(await readMarkdownFiles( + fullPath, + baseDir, + ignorePatterns, + visited, + )), + ); + } else if ( + isFile && + /\.mdx?$/.test(entry.name) && + !entry.name.startsWith('_') + ) { + files.push(fullPath); + } + } + + return files; + }; + + readMarkdownFiles.__symlinkPatched = true; + return readMarkdownFiles; +} + +// Plugin Patches (applied once at module load) + +if (!llmsUtils.resolvePartialImports?.__axPatched) { + const original = llmsUtils.resolvePartialImports; + llmsUtils.resolvePartialImports = async (...args) => { + currentMarkdownFilePath = args[1]; + return original(...args); + }; + llmsUtils.resolvePartialImports.__axPatched = true; +} + +// Plugin Export + +module.exports = function llmsSymlinksPlugin(context, options) { + if (!llmsUtils.readMarkdownFiles?.__symlinkPatched) { + llmsUtils.readMarkdownFiles = createSymlinkAwareReader(); + } + + if (!llmsProcessor.processFilesWithPatterns?.__axPatched) { + const original = llmsProcessor.processFilesWithPatterns; + llmsProcessor.processFilesWithPatterns = async (...args) => { + const docs = await original(...args); + return docs.map((doc) => + doc ? {...doc, description: deriveDescription(doc)} : doc, + ); + }; + llmsProcessor.processFilesWithPatterns.__axPatched = true; + } + + if (!llmsUtils.cleanMarkdownContent?.__axPatched) { + llmsUtils.cleanMarkdownContent = cleanMarkdownContent; + llmsUtils.cleanMarkdownContent.__axPatched = true; + } + + if (!llmsGenerator.generateStandardLLMFiles?.__axPatched) { + const originalProcessFilesWithPatterns = + llmsProcessor.processFilesWithPatterns; + + llmsGenerator.generateStandardLLMFiles = async (context, allDocFiles) => { + const {outDir, siteUrl, docTitle, docDescription, options} = context; + const { + generateLLMsTxt, + generateLLMsFullTxt, + llmsTxtFilename = 'llms.txt', + llmsFullTxtFilename = 'llms-full.txt', + includeOrder = [], + includeUnmatchedLast = true, + version, + generateMarkdownFiles = false, + rootContent, + fullRootContent, + } = options; + + if (!generateLLMsTxt && !generateLLMsFullTxt) return; + + // Process files using the (already patched) processor so descriptions are clean. + let processedDocs = await originalProcessFilesWithPatterns( + context, + allDocFiles, + [], // include all + [], // no extra ignore patterns + includeOrder, + includeUnmatchedLast, + ); + + console.log( + `Processed ${processedDocs.length} documentation files for standard LLM files`, + ); + + // Preserve optional per-doc markdown generation behavior. + if (generateMarkdownFiles && processedDocs.length > 0) { + console.log('Generating individual markdown files...'); + processedDocs = await llmsGenerator.generateIndividualMarkdownFiles( + processedDocs, + outDir, + siteUrl, + context.docsDir, + context.options.keepFrontMatter || [], + ); + } + + // Custom llms.txt generation with sentence/word-aware truncation. + if (generateLLMsTxt) { + const llmsTxtPath = path.join(outDir, llmsTxtFilename); + const versionInfo = version ? `\n\nVersion: ${version}` : ''; + const effectiveRootContent = + rootContent || + 'This file contains links to documentation sections following the llmstxt.org standard.'; + + const tocItems = processedDocs.map((doc) => { + const baseDescription = (doc.description || '').trim(); + const short = baseDescription + ? truncateDescription(baseDescription, 200) + : ''; + + return `- [${doc.title}](${doc.url})${short ? `: ${short}` : ''}`; + }); + + const llmFileContent = llmsUtils.createMarkdownContent( + docTitle, + `${docDescription}${versionInfo}`, + `${effectiveRootContent}\n\n## Table of Contents\n\n${tocItems.join( + '\n', + )}`, + true, + ); + + await llmsUtils.writeFile(llmsTxtPath, llmFileContent); + console.log(`Generated: ${llmsTxtPath}`); + } + + // Delegate llms-full.txt generation to the original helper. + if (generateLLMsFullTxt) { + const llmsFullTxtPath = path.join(outDir, llmsFullTxtFilename); + await llmsGenerator.generateLLMFile( + processedDocs, + llmsFullTxtPath, + docTitle, + docDescription, + true, // full content + version, + fullRootContent, + ); + console.log(`Generated: ${llmsFullTxtPath}`); + } + }; + + llmsGenerator.generateStandardLLMFiles.__axPatched = true; + } + + return llmsPlugin(context, options); +}; diff --git a/yarn.lock b/yarn.lock index aa046a6db6..23b9639453 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5810,6 +5810,15 @@ dns-packet@^5.2.2: dependencies: "@leichtgewicht/ip-codec" "^2.0.1" +docusaurus-plugin-llms@0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/docusaurus-plugin-llms/-/docusaurus-plugin-llms-0.2.2.tgz#3461e8295d18d4057cf0fbcf5e3feac561ea6fd1" + integrity sha512-DZlZ6cv9p5poFE00Qg78aurBNWhLa4o0VhH4kI33DUT0y4ydlFEJJbf8Bks9BuuGPFbY/Guebn+hRc2QymMImg== + dependencies: + gray-matter "^4.0.3" + minimatch "^9.0.3" + yaml "^2.8.1" + dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -9177,7 +9186,7 @@ minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" -minimatch@^9.0.4: +minimatch@^9.0.3, minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== @@ -11619,7 +11628,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11668,7 +11686,14 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -12629,7 +12654,16 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12714,6 +12748,11 @@ yaml@2.3.4: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== +yaml@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79" + integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== + yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"