@forwardimpact/libdoc
Advanced tools
+2
-2
@@ -157,4 +157,4 @@ #!/usr/bin/env node | ||
| const workingDir = process.env.INIT_CWD || process.cwd(); | ||
| const docsDir = path.join(workingDir, values.src); | ||
| const distDir = path.join(workingDir, values.out); | ||
| const docsDir = path.resolve(workingDir, values.src); | ||
| const distDir = path.resolve(workingDir, values.out); | ||
| const baseUrl = values["base-url"]; | ||
@@ -161,0 +161,0 @@ |
+18
-17
| { | ||
| "name": "@forwardimpact/libdoc", | ||
| "version": "0.2.23", | ||
| "version": "0.2.24", | ||
| "description": "Static documentation sites from markdown folders with front matter and navigation.", | ||
@@ -11,2 +11,10 @@ "keywords": [ | ||
| ], | ||
| "homepage": "https://www.forwardimpact.team", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/forwardimpact/monorepo.git", | ||
| "directory": "libraries/libdoc" | ||
| }, | ||
| "license": "Apache-2.0", | ||
| "author": "D. Olsson <hi@senzilla.io>", | ||
| "forwardimpact": { | ||
@@ -18,11 +26,10 @@ "capability": "agent-capability", | ||
| }, | ||
| "license": "Apache-2.0", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/forwardimpact/monorepo", | ||
| "directory": "libraries/libdoc" | ||
| }, | ||
| "homepage": "https://www.forwardimpact.team", | ||
| "type": "module", | ||
| "main": "./src/index.js", | ||
| "exports": { | ||
| ".": "./src/index.js", | ||
| "./builder": "./src/builder.js", | ||
| "./server": "./src/server.js", | ||
| "./frontmatter": "./src/frontmatter.js" | ||
| }, | ||
| "bin": { | ||
@@ -36,8 +43,2 @@ "fit-doc": "./bin/fit-doc.js" | ||
| ], | ||
| "exports": { | ||
| ".": "./src/index.js", | ||
| "./builder": "./src/builder.js", | ||
| "./server": "./src/server.js", | ||
| "./frontmatter": "./src/frontmatter.js" | ||
| }, | ||
| "scripts": { | ||
@@ -57,8 +58,8 @@ "test": "bun test test/*.test.js" | ||
| }, | ||
| "devDependencies": { | ||
| "@forwardimpact/libharness": "^0.1.14" | ||
| }, | ||
| "peerDependencies": { | ||
| "prettier": "^3.0.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@forwardimpact/libharness": "^0.1.14" | ||
| }, | ||
| "engines": { | ||
@@ -65,0 +66,0 @@ "bun": ">=1.2.0", |
+68
-43
@@ -102,3 +102,2 @@ import { gfmHeadingId } from "marked-gfm-heading-id"; | ||
| return html.replace( | ||
| // eslint-disable-next-line security/detect-unsafe-regex -- bounded negated char classes on internal HTML; no backtracking risk | ||
| /href="([^"]*?)\.md(#[^"]*)?"/g, | ||
@@ -121,3 +120,2 @@ (match, path, hash) => { | ||
| return markdown.replace( | ||
| // eslint-disable-next-line security/detect-unsafe-regex -- bounded negated char classes on internal markdown; no backtracking risk | ||
| /\[([^\]]*)\]\(([^)]*?)\.md(#[^)]*)?\)/g, | ||
@@ -255,22 +253,28 @@ (match, text, path, hash) => { | ||
| const fullPath = this.#path.join(dir, entryName); | ||
| this.#collectMarkdownEntry(fullPath, entryName, baseDir, results); | ||
| } | ||
| return results; | ||
| } | ||
| // Use statSync to check if it's a directory or file | ||
| try { | ||
| const stat = this.#fs.statSync(fullPath); | ||
| if (stat.isDirectory && stat.isDirectory()) { | ||
| results.push(...this.#findMarkdownFiles(fullPath, baseDir)); | ||
| } else if (entryName.endsWith(".md")) { | ||
| // Get path relative to base directory | ||
| const relativePath = fullPath.slice(baseDir.length + 1); | ||
| results.push(relativePath); | ||
| } | ||
| } catch { | ||
| // Skip files that can't be stat'd (e.g., template files) | ||
| if (entryName.endsWith(".md")) { | ||
| const relativePath = fullPath.slice(baseDir.length + 1); | ||
| results.push(relativePath); | ||
| } | ||
| /** | ||
| * Classify a single directory entry and collect it (or recurse) into results | ||
| * @param {string} fullPath - Absolute path to the entry | ||
| * @param {string} entryName - Basename of the entry | ||
| * @param {string} baseDir - Root directory for relative path computation | ||
| * @param {string[]} results - Accumulator for relative markdown paths | ||
| */ | ||
| #collectMarkdownEntry(fullPath, entryName, baseDir, results) { | ||
| try { | ||
| const stat = this.#fs.statSync(fullPath); | ||
| if (stat.isDirectory && stat.isDirectory()) { | ||
| results.push(...this.#findMarkdownFiles(fullPath, baseDir)); | ||
| } else if (entryName.endsWith(".md")) { | ||
| results.push(fullPath.slice(baseDir.length + 1)); | ||
| } | ||
| } catch { | ||
| // Skip files that can't be stat'd (e.g., template files) | ||
| if (entryName.endsWith(".md")) { | ||
| results.push(fullPath.slice(baseDir.length + 1)); | ||
| } | ||
| } | ||
| return results; | ||
| } | ||
@@ -304,15 +308,7 @@ | ||
| /** | ||
| * Augment llms.txt with auto-generated page links under each H2 section | ||
| * @param {Array<{urlPath: string, title: string, description: string}>} pages - Sorted page inventory | ||
| * @param {string} baseUrl - Base URL for the site | ||
| * @param {string} distDir - Destination distribution directory | ||
| * Classify pages into Products / Documentation / Optional buckets | ||
| * @param {Array<{urlPath: string}>} pages - Page inventory | ||
| * @returns {Object<string, Array>} | ||
| */ | ||
| #augmentLlmsTxt(pages, baseUrl, distDir) { | ||
| const llmsPath = this.#path.join(distDir, "llms.txt"); | ||
| if (!this.#fs.existsSync(llmsPath)) return; | ||
| const content = this.#fs.readFileSync(llmsPath, "utf-8"); | ||
| const lines = content.split("\n"); | ||
| // Map pages to sections | ||
| #classifyPagesIntoSections(pages) { | ||
| const sections = { Products: [], Documentation: [], Optional: [] }; | ||
@@ -339,13 +335,13 @@ const productSlugs = new Set([ | ||
| } | ||
| return sections; | ||
| } | ||
| const linkLine = (page) => { | ||
| const mdUrl = | ||
| page.urlPath === "/" | ||
| ? `${baseUrl}/index.md` | ||
| : `${baseUrl}${page.urlPath}index.md`; | ||
| const desc = page.description ? `: ${page.description}` : ""; | ||
| return `- [${page.title}](${mdUrl})${desc}`; | ||
| }; | ||
| // Reassemble: insert links after each H2 | ||
| /** | ||
| * Insert page links after matching H2 headings | ||
| * @param {string[]} lines - Original llms.txt lines | ||
| * @param {Object<string, Array>} sections - Classified page buckets | ||
| * @param {Function} linkLine - Formats a page as a markdown link line | ||
| * @returns {string[]} Augmented lines | ||
| */ | ||
| #insertSectionLinks(lines, sections, linkLine) { | ||
| const output = []; | ||
@@ -356,4 +352,3 @@ for (const line of lines) { | ||
| if (h2Match) { | ||
| const sectionName = h2Match[1].trim(); | ||
| const pageList = sections[sectionName]; | ||
| const pageList = sections[h2Match[1].trim()]; | ||
| if (pageList?.length) { | ||
@@ -367,3 +362,33 @@ output.push(""); | ||
| } | ||
| return output; | ||
| } | ||
| /** | ||
| * Augment llms.txt with auto-generated page links under each H2 section | ||
| * @param {Array<{urlPath: string, title: string, description: string}>} pages - Sorted page inventory | ||
| * @param {string} baseUrl - Base URL for the site | ||
| * @param {string} distDir - Destination distribution directory | ||
| */ | ||
| #augmentLlmsTxt(pages, baseUrl, distDir) { | ||
| const llmsPath = this.#path.join(distDir, "llms.txt"); | ||
| if (!this.#fs.existsSync(llmsPath)) return; | ||
| const content = this.#fs.readFileSync(llmsPath, "utf-8"); | ||
| const sections = this.#classifyPagesIntoSections(pages); | ||
| const linkLine = (page) => { | ||
| const mdUrl = | ||
| page.urlPath === "/" | ||
| ? `${baseUrl}/index.md` | ||
| : `${baseUrl}${page.urlPath}index.md`; | ||
| const desc = page.description ? `: ${page.description}` : ""; | ||
| return `- [${page.title}](${mdUrl})${desc}`; | ||
| }; | ||
| const output = this.#insertSectionLinks( | ||
| content.split("\n"), | ||
| sections, | ||
| linkLine, | ||
| ); | ||
| this.#fs.writeFileSync(llmsPath, output.join("\n"), "utf-8"); | ||
@@ -370,0 +395,0 @@ logger.info(" ✓ llms.txt (augmented)"); |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
44864
1.57%908
2.71%1
-50%