Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@forwardimpact/libdoc

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@forwardimpact/libdoc - npm Package Compare versions

Comparing version
0.2.23
to
0.2.24
+2
-2
bin/fit-doc.js

@@ -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 @@

{
"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",

@@ -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)");