doxla 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  # doxla
6
6
 
7
+ [![AI-Agent Readiness](https://ready.kerex.app/api/badge/alsiola/doxla)](https://ready.kerex.app/analysis/alsiola/doxla)
8
+
7
9
  Improve documentation discoverability within repos. Doxla discovers all `.md` and `.mdx` files in your repository, builds a beautiful docs viewer, and deploys it to GitHub Pages.
8
10
 
9
11
  ## Why?
package/dist/cli/index.js CHANGED
@@ -102,23 +102,45 @@ async function discoverMarkdownFiles(rootDir) {
102
102
 
103
103
  // src/cli/lib/manifest.ts
104
104
  import { readFile } from "fs/promises";
105
- import { join as join2, basename, dirname } from "path";
105
+ import { join as join2, basename, dirname, normalize } from "path";
106
+ function resolveImagePaths(content, docPath) {
107
+ const docDir = dirname(docPath);
108
+ const images = [];
109
+ function resolve3(src) {
110
+ if (/^(https?:\/\/|data:)/.test(src)) return null;
111
+ const resolved = normalize(join2(docDir, src)).replace(/\\/g, "/");
112
+ if (resolved.startsWith("..")) return null;
113
+ images.push(resolved);
114
+ return resolved;
115
+ }
116
+ let result = content.replace(
117
+ /!\[([^\]]*)\]\(([^)]+)\)/g,
118
+ (match, alt, src) => {
119
+ const resolved = resolve3(src);
120
+ return resolved ? `![${alt}](${resolved})` : match;
121
+ }
122
+ );
123
+ result = result.replace(
124
+ /(<img\s[^>]*?)src=["']([^"']+)["']/g,
125
+ (match, prefix, src) => {
126
+ const resolved = resolve3(src);
127
+ return resolved ? `${prefix}src="${resolved}"` : match;
128
+ }
129
+ );
130
+ return { content: result, images: [...new Set(images)] };
131
+ }
106
132
  function extractTitleAndContent(content, filePath) {
107
133
  const lines = content.split("\n");
108
134
  for (let i = 0; i < lines.length; i++) {
109
135
  if (lines[i].trim() === "") continue;
110
- const match2 = lines[i].match(/^#\s+(.+)$/);
111
- if (match2) {
112
- const title = match2[1].trim();
136
+ const match = lines[i].match(/^#\s+(.+)$/);
137
+ if (match) {
138
+ const title = match[1].trim();
113
139
  const remaining = lines.slice(i + 1).join("\n").trimStart();
114
140
  return { title, content: remaining };
115
141
  }
116
142
  break;
117
143
  }
118
- const match = content.match(/^#\s+(.+)$/m);
119
- if (match) {
120
- return { title: match[1].trim(), content };
121
- }
122
144
  const ext = filePath.endsWith(".mdx") ? ".mdx" : ".md";
123
145
  const name = basename(filePath, ext);
124
146
  if (name.toLowerCase() === "readme") {
@@ -135,25 +157,31 @@ function createSlug(filePath) {
135
157
  return filePath.replace(/\.mdx?$/i, "").replace(/\\/g, "/").toLowerCase().replace(/[^a-z0-9/.-]/g, "-");
136
158
  }
137
159
  async function generateManifest(rootDir, files) {
160
+ const allImages = [];
138
161
  const docs = await Promise.all(
139
162
  files.map(async (filePath) => {
140
163
  const fullPath = join2(rootDir, filePath);
141
164
  const raw = await readFile(fullPath, "utf-8");
142
- const { title, content } = extractTitleAndContent(raw, filePath);
165
+ const extracted = extractTitleAndContent(raw, filePath);
166
+ const { content, images } = resolveImagePaths(extracted.content, filePath);
167
+ allImages.push(...images);
143
168
  return {
144
169
  slug: createSlug(filePath),
145
170
  path: filePath,
146
- title,
171
+ title: extracted.title,
147
172
  content
148
173
  };
149
174
  })
150
175
  );
151
176
  const repoName = basename(rootDir);
152
177
  return {
153
- repoName,
154
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
155
- totalDocs: docs.length,
156
- docs
178
+ manifest: {
179
+ repoName,
180
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
181
+ totalDocs: docs.length,
182
+ docs
183
+ },
184
+ images: [...new Set(allImages)]
157
185
  };
158
186
  }
159
187
 
@@ -196,6 +224,17 @@ async function buildApp(manifest, options) {
196
224
  JSON.stringify(manifest, null, 2),
197
225
  "utf-8"
198
226
  );
227
+ if (options.images.length > 0) {
228
+ const publicDir = join3(tempDir, "public");
229
+ await mkdir2(publicDir, { recursive: true });
230
+ for (const img of options.images) {
231
+ const src = join3(options.rootDir, img);
232
+ const dest = join3(publicDir, img);
233
+ await mkdir2(join3(dest, ".."), { recursive: true });
234
+ await cp(src, dest).catch(() => {
235
+ });
236
+ }
237
+ }
199
238
  execSync("npm install --no-audit --no-fund", {
200
239
  cwd: tempDir,
201
240
  stdio: "pipe"
@@ -229,11 +268,11 @@ async function buildCommand(options) {
229
268
  }
230
269
  discoverSpinner.succeed(`Found ${files.length} markdown file${files.length === 1 ? "" : "s"}`);
231
270
  const manifestSpinner = ora("Generating manifest...").start();
232
- const manifest = await generateManifest(rootDir, files);
271
+ const { manifest, images } = await generateManifest(rootDir, files);
233
272
  manifestSpinner.succeed("Manifest generated");
234
273
  const buildSpinner = ora("Building docs viewer...").start();
235
274
  try {
236
- await buildApp(manifest, { output: outputDir, basePath });
275
+ await buildApp(manifest, { output: outputDir, basePath, rootDir, images });
237
276
  buildSpinner.succeed("Docs viewer built");
238
277
  } catch (error) {
239
278
  buildSpinner.fail("Build failed");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/init.ts","../../src/cli/templates/doxla-workflow.ts","../../src/cli/commands/build.ts","../../src/cli/lib/discover.ts","../../src/cli/lib/manifest.ts","../../src/cli/lib/build-app.ts","../../src/cli/lib/template.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { buildCommand } from \"./commands/build.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"doxla\")\n .description(\"Improve documentation discoverability within repos\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"init\")\n .description(\"Set up GitHub Actions workflow for Doxla docs deployment\")\n .action(initCommand);\n\nprogram\n .command(\"build\")\n .description(\"Discover markdown files and build the docs viewer\")\n .option(\"-o, --output <dir>\", \"Output directory\", \"doxla-dist\")\n .option(\"-r, --root <dir>\", \"Root directory to scan for markdown files\", \".\")\n .option(\"--base-path <path>\", \"Base path for GitHub Pages deployment\", \"/\")\n .action(buildCommand);\n\nprogram.parse();\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport { workflowTemplate } from \"../templates/doxla-workflow.js\";\n\nexport async function initCommand() {\n const workflowDir = join(process.cwd(), \".github\", \"workflows\");\n const workflowPath = join(workflowDir, \"doxla.yml\");\n\n if (existsSync(workflowPath)) {\n console.log(\n chalk.yellow(\"⚠ Workflow file already exists at .github/workflows/doxla.yml\")\n );\n console.log(chalk.yellow(\" Overwriting with latest template...\"));\n }\n\n await mkdir(workflowDir, { recursive: true });\n await writeFile(workflowPath, workflowTemplate, \"utf-8\");\n\n console.log(chalk.green(\"✓ Created .github/workflows/doxla.yml\"));\n console.log();\n console.log(\"Next steps:\");\n console.log(\n ` 1. Enable GitHub Pages in your repo settings (Settings → Pages → Source: ${chalk.bold(\"GitHub Actions\")})`\n );\n console.log(\" 2. Commit and push the workflow file\");\n console.log(\n \" 3. Your docs will be built and deployed on every push to main\"\n );\n}\n","export const workflowTemplate = `name: Deploy Doxla Docs\n\non:\n push:\n branches: [main]\n workflow_dispatch:\n\npermissions:\n contents: read\n pages: write\n id-token: write\n\nconcurrency:\n group: \"pages\"\n cancel-in-progress: false\n\njobs:\n build-and-deploy:\n runs-on: ubuntu-latest\n environment:\n name: github-pages\n url: \\${{ steps.deployment.outputs.page_url }}\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: \"20\"\n - run: npx doxla@latest build --base-path \"/\\${{ github.event.repository.name }}\"\n - uses: actions/upload-pages-artifact@v3\n with:\n path: ./doxla-dist\n - id: deployment\n uses: actions/deploy-pages@v4\n`;\n","import { resolve } from \"node:path\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { discoverMarkdownFiles } from \"../lib/discover.js\";\nimport { generateManifest } from \"../lib/manifest.js\";\nimport { buildApp } from \"../lib/build-app.js\";\n\ninterface BuildOptions {\n output: string;\n root: string;\n basePath: string;\n}\n\nexport async function buildCommand(options: BuildOptions) {\n const rootDir = resolve(options.root);\n const outputDir = resolve(options.output);\n const basePath = options.basePath;\n\n console.log(chalk.bold(\"doxla build\"));\n console.log();\n\n // Step 1: Discover markdown files\n const discoverSpinner = ora(\"Discovering markdown files...\").start();\n const files = await discoverMarkdownFiles(rootDir);\n\n if (files.length === 0) {\n discoverSpinner.fail(\"No markdown files found\");\n process.exit(1);\n }\n\n discoverSpinner.succeed(`Found ${files.length} markdown file${files.length === 1 ? \"\" : \"s\"}`);\n\n // Step 2: Generate manifest\n const manifestSpinner = ora(\"Generating manifest...\").start();\n const manifest = await generateManifest(rootDir, files);\n manifestSpinner.succeed(\"Manifest generated\");\n\n // Step 3: Build docs viewer\n const buildSpinner = ora(\"Building docs viewer...\").start();\n try {\n await buildApp(manifest, { output: outputDir, basePath });\n buildSpinner.succeed(\"Docs viewer built\");\n } catch (error) {\n buildSpinner.fail(\"Build failed\");\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.green(`✓ Output written to ${options.output}/`));\n}\n","import fg from \"fast-glob\";\n\nconst IGNORE_PATTERNS = [\n \"**/node_modules/**\",\n \"**/.git/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/doxla-dist/**\",\n \"**/.next/**\",\n \"**/coverage/**\",\n];\n\nexport async function discoverMarkdownFiles(rootDir: string): Promise<string[]> {\n const files = await fg(\"**/*.{md,mdx}\", {\n cwd: rootDir,\n ignore: IGNORE_PATTERNS,\n onlyFiles: true,\n });\n\n // Sort: README.md/README.mdx first, then alphabetical\n return files.sort((a, b) => {\n const aIsReadme = /^readme\\.mdx?$/i.test(a);\n const bIsReadme = /^readme\\.mdx?$/i.test(b);\n if (aIsReadme && !bIsReadme) return -1;\n if (!aIsReadme && bIsReadme) return 1;\n return a.localeCompare(b);\n });\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join, basename, dirname } from \"node:path\";\nimport type { Manifest, DocFile } from \"../../app/src/types/manifest.js\";\n\nfunction extractTitleAndContent(\n content: string,\n filePath: string\n): { title: string; content: string } {\n // Check if the first non-blank line is a # heading\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].trim() === \"\") continue;\n\n const match = lines[i].match(/^#\\s+(.+)$/);\n if (match) {\n const title = match[1].trim();\n const remaining = lines.slice(i + 1).join(\"\\n\").trimStart();\n return { title, content: remaining };\n }\n break;\n }\n\n // No leading heading — try any heading in the document for the title\n const match = content.match(/^#\\s+(.+)$/m);\n if (match) {\n return { title: match[1].trim(), content };\n }\n\n // Fallback to filename without extension\n const ext = filePath.endsWith(\".mdx\") ? \".mdx\" : \".md\";\n const name = basename(filePath, ext);\n if (name.toLowerCase() === \"readme\") {\n const dir = dirname(filePath);\n if (dir === \".\") return { title: \"README\", content };\n return { title: `${dir} - README`, content };\n }\n return {\n title: name.replace(/[-_]/g, \" \").replace(/\\b\\w/g, (c) => c.toUpperCase()),\n content,\n };\n}\n\nfunction createSlug(filePath: string): string {\n return filePath\n .replace(/\\.mdx?$/i, \"\")\n .replace(/\\\\/g, \"/\")\n .toLowerCase()\n .replace(/[^a-z0-9/.-]/g, \"-\");\n}\n\nexport async function generateManifest(\n rootDir: string,\n files: string[]\n): Promise<Manifest> {\n const docs: DocFile[] = await Promise.all(\n files.map(async (filePath) => {\n const fullPath = join(rootDir, filePath);\n const raw = await readFile(fullPath, \"utf-8\");\n const { title, content } = extractTitleAndContent(raw, filePath);\n return {\n slug: createSlug(filePath),\n path: filePath,\n title,\n content,\n };\n })\n );\n\n const repoName = basename(rootDir);\n\n return {\n repoName,\n generatedAt: new Date().toISOString(),\n totalDocs: docs.length,\n docs,\n };\n}\n","import { cp, writeFile, rm, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport type { Manifest } from \"../../app/src/types/manifest.js\";\nimport { getTemplatePath } from \"./template.js\";\n\ninterface BuildOptions {\n output: string;\n basePath: string;\n}\n\nexport async function buildApp(\n manifest: Manifest,\n options: BuildOptions\n): Promise<void> {\n const templateDir = getTemplatePath();\n const tempDir = join(\n tmpdir(),\n `doxla-build-${randomBytes(6).toString(\"hex\")}`\n );\n\n try {\n // Copy template to temp dir\n await cp(templateDir, tempDir, { recursive: true });\n\n // Write manifest.json into the app's src directory\n await writeFile(\n join(tempDir, \"src\", \"manifest.json\"),\n JSON.stringify(manifest, null, 2),\n \"utf-8\"\n );\n\n // Install dependencies\n execSync(\"npm install --no-audit --no-fund\", {\n cwd: tempDir,\n stdio: \"pipe\",\n });\n\n // Build with Vite, passing base path as env var\n execSync(\"npx vite build\", {\n cwd: tempDir,\n stdio: \"pipe\",\n env: { ...process.env, VITE_BASE_PATH: options.basePath },\n });\n\n // Copy output\n await rm(options.output, { recursive: true, force: true });\n await mkdir(options.output, { recursive: true });\n await cp(join(tempDir, \"dist\"), options.output, { recursive: true });\n } finally {\n // Clean up temp dir\n await rm(tempDir, { recursive: true, force: true }).catch(() => {});\n }\n}\n","import { fileURLToPath } from \"node:url\";\nimport { dirname, resolve } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\nexport function getTemplatePath(): string {\n const __filename = fileURLToPath(import.meta.url);\n let dir = dirname(__filename);\n\n // Walk up from the current file location until we find src/app\n // Works both from source (src/cli/lib/) and built (dist/cli/)\n for (let i = 0; i < 5; i++) {\n const candidate = resolve(dir, \"src\", \"app\");\n if (existsSync(resolve(candidate, \"package.json\"))) {\n return candidate;\n }\n dir = dirname(dir);\n }\n\n // Fallback: assume built location dist/cli/ -> ../../src/app\n const fallback = resolve(dirname(__filename), \"..\", \"..\", \"src\", \"app\");\n return fallback;\n}\n"],"mappings":"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","names":["resolve","chalk","join","match","writeFile","mkdir","join","dirname","existsSync","join","writeFile","mkdir","resolve","chalk"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/init.ts","../../src/cli/templates/doxla-workflow.ts","../../src/cli/commands/build.ts","../../src/cli/lib/discover.ts","../../src/cli/lib/manifest.ts","../../src/cli/lib/build-app.ts","../../src/cli/lib/template.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { buildCommand } from \"./commands/build.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"doxla\")\n .description(\"Improve documentation discoverability within repos\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"init\")\n .description(\"Set up GitHub Actions workflow for Doxla docs deployment\")\n .action(initCommand);\n\nprogram\n .command(\"build\")\n .description(\"Discover markdown files and build the docs viewer\")\n .option(\"-o, --output <dir>\", \"Output directory\", \"doxla-dist\")\n .option(\"-r, --root <dir>\", \"Root directory to scan for markdown files\", \".\")\n .option(\"--base-path <path>\", \"Base path for GitHub Pages deployment\", \"/\")\n .action(buildCommand);\n\nprogram.parse();\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport { workflowTemplate } from \"../templates/doxla-workflow.js\";\n\nexport async function initCommand() {\n const workflowDir = join(process.cwd(), \".github\", \"workflows\");\n const workflowPath = join(workflowDir, \"doxla.yml\");\n\n if (existsSync(workflowPath)) {\n console.log(\n chalk.yellow(\"⚠ Workflow file already exists at .github/workflows/doxla.yml\")\n );\n console.log(chalk.yellow(\" Overwriting with latest template...\"));\n }\n\n await mkdir(workflowDir, { recursive: true });\n await writeFile(workflowPath, workflowTemplate, \"utf-8\");\n\n console.log(chalk.green(\"✓ Created .github/workflows/doxla.yml\"));\n console.log();\n console.log(\"Next steps:\");\n console.log(\n ` 1. Enable GitHub Pages in your repo settings (Settings → Pages → Source: ${chalk.bold(\"GitHub Actions\")})`\n );\n console.log(\" 2. Commit and push the workflow file\");\n console.log(\n \" 3. Your docs will be built and deployed on every push to main\"\n );\n}\n","export const workflowTemplate = `name: Deploy Doxla Docs\n\non:\n push:\n branches: [main]\n workflow_dispatch:\n\npermissions:\n contents: read\n pages: write\n id-token: write\n\nconcurrency:\n group: \"pages\"\n cancel-in-progress: false\n\njobs:\n build-and-deploy:\n runs-on: ubuntu-latest\n environment:\n name: github-pages\n url: \\${{ steps.deployment.outputs.page_url }}\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: \"20\"\n - run: npx doxla@latest build --base-path \"/\\${{ github.event.repository.name }}\"\n - uses: actions/upload-pages-artifact@v3\n with:\n path: ./doxla-dist\n - id: deployment\n uses: actions/deploy-pages@v4\n`;\n","import { resolve } from \"node:path\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { discoverMarkdownFiles } from \"../lib/discover.js\";\nimport { generateManifest } from \"../lib/manifest.js\";\nimport { buildApp } from \"../lib/build-app.js\";\n\ninterface BuildOptions {\n output: string;\n root: string;\n basePath: string;\n}\n\nexport async function buildCommand(options: BuildOptions) {\n const rootDir = resolve(options.root);\n const outputDir = resolve(options.output);\n const basePath = options.basePath;\n\n console.log(chalk.bold(\"doxla build\"));\n console.log();\n\n // Step 1: Discover markdown files\n const discoverSpinner = ora(\"Discovering markdown files...\").start();\n const files = await discoverMarkdownFiles(rootDir);\n\n if (files.length === 0) {\n discoverSpinner.fail(\"No markdown files found\");\n process.exit(1);\n }\n\n discoverSpinner.succeed(`Found ${files.length} markdown file${files.length === 1 ? \"\" : \"s\"}`);\n\n // Step 2: Generate manifest\n const manifestSpinner = ora(\"Generating manifest...\").start();\n const { manifest, images } = await generateManifest(rootDir, files);\n manifestSpinner.succeed(\"Manifest generated\");\n\n // Step 3: Build docs viewer\n const buildSpinner = ora(\"Building docs viewer...\").start();\n try {\n await buildApp(manifest, { output: outputDir, basePath, rootDir, images });\n buildSpinner.succeed(\"Docs viewer built\");\n } catch (error) {\n buildSpinner.fail(\"Build failed\");\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.green(`✓ Output written to ${options.output}/`));\n}\n","import fg from \"fast-glob\";\n\nconst IGNORE_PATTERNS = [\n \"**/node_modules/**\",\n \"**/.git/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/doxla-dist/**\",\n \"**/.next/**\",\n \"**/coverage/**\",\n];\n\nexport async function discoverMarkdownFiles(rootDir: string): Promise<string[]> {\n const files = await fg(\"**/*.{md,mdx}\", {\n cwd: rootDir,\n ignore: IGNORE_PATTERNS,\n onlyFiles: true,\n });\n\n // Sort: README.md/README.mdx first, then alphabetical\n return files.sort((a, b) => {\n const aIsReadme = /^readme\\.mdx?$/i.test(a);\n const bIsReadme = /^readme\\.mdx?$/i.test(b);\n if (aIsReadme && !bIsReadme) return -1;\n if (!aIsReadme && bIsReadme) return 1;\n return a.localeCompare(b);\n });\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join, basename, dirname, normalize } from \"node:path\";\nimport type { Manifest, DocFile } from \"../../app/src/types/manifest.js\";\n\nfunction resolveImagePaths(\n content: string,\n docPath: string\n): { content: string; images: string[] } {\n const docDir = dirname(docPath);\n const images: string[] = [];\n\n function resolve(src: string): string | null {\n if (/^(https?:\\/\\/|data:)/.test(src)) return null;\n const resolved = normalize(join(docDir, src)).replace(/\\\\/g, \"/\");\n if (resolved.startsWith(\"..\")) return null;\n images.push(resolved);\n return resolved;\n }\n\n // Rewrite markdown images: ![alt](path)\n let result = content.replace(\n /!\\[([^\\]]*)\\]\\(([^)]+)\\)/g,\n (match, alt, src) => {\n const resolved = resolve(src);\n return resolved ? `![${alt}](${resolved})` : match;\n }\n );\n\n // Rewrite HTML images: <img ... src=\"path\" ...>\n result = result.replace(\n /(<img\\s[^>]*?)src=[\"']([^\"']+)[\"']/g,\n (match, prefix, src) => {\n const resolved = resolve(src);\n return resolved ? `${prefix}src=\"${resolved}\"` : match;\n }\n );\n\n return { content: result, images: [...new Set(images)] };\n}\n\nfunction extractTitleAndContent(\n content: string,\n filePath: string\n): { title: string; content: string } {\n // Check if the first non-blank line is a # heading\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].trim() === \"\") continue;\n\n const match = lines[i].match(/^#\\s+(.+)$/);\n if (match) {\n const title = match[1].trim();\n const remaining = lines.slice(i + 1).join(\"\\n\").trimStart();\n return { title, content: remaining };\n }\n break;\n }\n\n // No leading heading — fallback to filename\n const ext = filePath.endsWith(\".mdx\") ? \".mdx\" : \".md\";\n const name = basename(filePath, ext);\n if (name.toLowerCase() === \"readme\") {\n const dir = dirname(filePath);\n if (dir === \".\") return { title: \"README\", content };\n return { title: `${dir} - README`, content };\n }\n return {\n title: name.replace(/[-_]/g, \" \").replace(/\\b\\w/g, (c) => c.toUpperCase()),\n content,\n };\n}\n\nfunction createSlug(filePath: string): string {\n return filePath\n .replace(/\\.mdx?$/i, \"\")\n .replace(/\\\\/g, \"/\")\n .toLowerCase()\n .replace(/[^a-z0-9/.-]/g, \"-\");\n}\n\nexport async function generateManifest(\n rootDir: string,\n files: string[]\n): Promise<{ manifest: Manifest; images: string[] }> {\n const allImages: string[] = [];\n\n const docs: DocFile[] = await Promise.all(\n files.map(async (filePath) => {\n const fullPath = join(rootDir, filePath);\n const raw = await readFile(fullPath, \"utf-8\");\n const extracted = extractTitleAndContent(raw, filePath);\n const { content, images } = resolveImagePaths(extracted.content, filePath);\n allImages.push(...images);\n return {\n slug: createSlug(filePath),\n path: filePath,\n title: extracted.title,\n content,\n };\n })\n );\n\n const repoName = basename(rootDir);\n\n return {\n manifest: {\n repoName,\n generatedAt: new Date().toISOString(),\n totalDocs: docs.length,\n docs,\n },\n images: [...new Set(allImages)],\n };\n}\n","import { cp, writeFile, rm, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport type { Manifest } from \"../../app/src/types/manifest.js\";\nimport { getTemplatePath } from \"./template.js\";\n\ninterface BuildOptions {\n output: string;\n basePath: string;\n rootDir: string;\n images: string[];\n}\n\nexport async function buildApp(\n manifest: Manifest,\n options: BuildOptions\n): Promise<void> {\n const templateDir = getTemplatePath();\n const tempDir = join(\n tmpdir(),\n `doxla-build-${randomBytes(6).toString(\"hex\")}`\n );\n\n try {\n // Copy template to temp dir\n await cp(templateDir, tempDir, { recursive: true });\n\n // Write manifest.json into the app's src directory\n await writeFile(\n join(tempDir, \"src\", \"manifest.json\"),\n JSON.stringify(manifest, null, 2),\n \"utf-8\"\n );\n\n // Copy referenced images to public dir\n if (options.images.length > 0) {\n const publicDir = join(tempDir, \"public\");\n await mkdir(publicDir, { recursive: true });\n for (const img of options.images) {\n const src = join(options.rootDir, img);\n const dest = join(publicDir, img);\n await mkdir(join(dest, \"..\"), { recursive: true });\n await cp(src, dest).catch(() => {});\n }\n }\n\n // Install dependencies\n execSync(\"npm install --no-audit --no-fund\", {\n cwd: tempDir,\n stdio: \"pipe\",\n });\n\n // Build with Vite, passing base path as env var\n execSync(\"npx vite build\", {\n cwd: tempDir,\n stdio: \"pipe\",\n env: { ...process.env, VITE_BASE_PATH: options.basePath },\n });\n\n // Copy output\n await rm(options.output, { recursive: true, force: true });\n await mkdir(options.output, { recursive: true });\n await cp(join(tempDir, \"dist\"), options.output, { recursive: true });\n } finally {\n // Clean up temp dir\n await rm(tempDir, { recursive: true, force: true }).catch(() => {});\n }\n}\n","import { fileURLToPath } from \"node:url\";\nimport { dirname, resolve } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\nexport function getTemplatePath(): string {\n const __filename = fileURLToPath(import.meta.url);\n let dir = dirname(__filename);\n\n // Walk up from the current file location until we find src/app\n // Works both from source (src/cli/lib/) and built (dist/cli/)\n for (let i = 0; i < 5; i++) {\n const candidate = resolve(dir, \"src\", \"app\");\n if (existsSync(resolve(candidate, \"package.json\"))) {\n return candidate;\n }\n dir = dirname(dir);\n }\n\n // Fallback: assume built location dist/cli/ -> ../../src/app\n const fallback = resolve(dirname(__filename), \"..\", \"..\", \"src\", \"app\");\n return fallback;\n}\n"],"mappings":"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","names":["resolve","chalk","join","resolve","writeFile","mkdir","join","dirname","existsSync","join","writeFile","mkdir","resolve","chalk"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doxla",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Improve documentation discoverability within repos",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,7 +38,8 @@
38
38
  "chalk": "^5.4.1",
39
39
  "commander": "^14.0.3",
40
40
  "fast-glob": "^3.3.3",
41
- "ora": "^9.3.0"
41
+ "ora": "^9.3.0",
42
+ "rehype-raw": "^7.0.0"
42
43
  },
43
44
  "devDependencies": {
44
45
  "@mdx-js/mdx": "^3.1.0",
@@ -11,6 +11,7 @@
11
11
  "react-dom": "^19.0.0",
12
12
  "@mdx-js/mdx": "^3.1.0",
13
13
  "react-markdown": "^10.1.0",
14
+ "rehype-raw": "^7.0.0",
14
15
  "remark-gfm": "^4.0.0",
15
16
  "react-syntax-highlighter": "^15.6.1",
16
17
  "lucide-react": "^0.474.0",
@@ -1,5 +1,6 @@
1
1
  import { useMemo } from "react";
2
2
  import Markdown from "react-markdown";
3
+ import rehypeRaw from "rehype-raw";
3
4
  import remarkGfm from "remark-gfm";
4
5
  import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
5
6
  import { oneLight, oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";
@@ -61,7 +62,7 @@ export function MarkdownRenderer({ content, theme, docPath }: MarkdownRendererPr
61
62
 
62
63
  return (
63
64
  <div className={`prose prose-neutral max-w-none ${theme === "dark" ? "prose-invert" : ""}`}>
64
- <Markdown remarkPlugins={[remarkGfm]} components={components}>
65
+ <Markdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} components={components}>
65
66
  {content}
66
67
  </Markdown>
67
68
  </div>