diff --git a/package.json b/package.json index 037b4e836f8..7a9d8b36747 100644 --- a/package.json +++ b/package.json @@ -1317,8 +1317,8 @@ "format:all": "pnpm format && pnpm format:swift", "format:check": "oxfmt --check --threads=1", "format:diff": "oxfmt --write --threads=1 && git --no-pager diff", - "format:docs": "git ls-files 'docs/**/*.md' 'docs/**/*.mdx' 'README.md' | xargs oxfmt --write --threads=1", - "format:docs:check": "git ls-files 'docs/**/*.md' 'docs/**/*.mdx' 'README.md' | xargs oxfmt --check --threads=1", + "format:docs": "node scripts/format-docs.mjs", + "format:docs:check": "node scripts/format-docs.mjs --check", "format:fix": "oxfmt --write --threads=1", "format:swift": "swiftformat --lint --config .swiftformat apps/macos/Sources apps/ios/Sources apps/shared/OpenClawKit/Sources", "gateway:dev": "OPENCLAW_SKIP_CHANNELS=1 node scripts/run-node.mjs --dev gateway", diff --git a/scripts/format-docs.mjs b/scripts/format-docs.mjs new file mode 100644 index 00000000000..92992a84821 --- /dev/null +++ b/scripts/format-docs.mjs @@ -0,0 +1,100 @@ +#!/usr/bin/env node + +import { execFileSync, spawnSync } from "node:child_process"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { repairMintlifyAccordionIndentation } from "./lib/mintlify-accordion.mjs"; + +const ROOT = path.resolve(import.meta.dirname, ".."); +const CHECK = process.argv.includes("--check"); +const OXFMT_BIN = path.join(ROOT, "node_modules", "oxfmt", "bin", "oxfmt"); +const OXFMT_CONFIG = path.join(ROOT, ".oxfmtrc.jsonc"); + +function docsFiles() { + const output = execFileSync("git", ["ls-files", "docs/**/*.md", "docs/**/*.mdx", "README.md"], { + cwd: ROOT, + encoding: "utf8", + }); + return output.split("\n").filter(Boolean); +} + +function runOxfmt(files) { + const result = spawnSync( + process.execPath, + [OXFMT_BIN, "--write", "--threads=1", "--config", OXFMT_CONFIG, ...files], + { + cwd: ROOT, + encoding: "utf8", + maxBuffer: 1024 * 1024 * 16, + }, + ); + + if (result.status !== 0) { + const stderr = result.stderr.trim(); + throw new Error(`oxfmt failed${stderr ? `:\n${stderr}` : ""}`); + } +} + +function repairFiles(root, files) { + const changed = []; + for (const relativePath of files) { + const absolutePath = path.join(root, relativePath); + const raw = fs.readFileSync(absolutePath, "utf8"); + const formatted = repairMintlifyAccordionIndentation(raw); + if (formatted === raw) { + continue; + } + fs.writeFileSync(absolutePath, formatted); + changed.push(relativePath); + } + return changed; +} + +function copyDocsToTemp(files) { + const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-docs-format-")); + for (const relativePath of files) { + const source = path.join(ROOT, relativePath); + const target = path.join(tempRoot, relativePath); + fs.mkdirSync(path.dirname(target), { recursive: true }); + fs.copyFileSync(source, target); + } + return tempRoot; +} + +const changed = []; +const files = docsFiles(); + +if (CHECK) { + const tempRoot = copyDocsToTemp(files); + try { + runOxfmt(files.map((relativePath) => path.join(tempRoot, relativePath))); + repairFiles(tempRoot, files); + for (const relativePath of files) { + const raw = fs.readFileSync(path.join(ROOT, relativePath), "utf8"); + const formatted = fs.readFileSync(path.join(tempRoot, relativePath), "utf8"); + if (formatted !== raw) { + changed.push(relativePath); + } + } + } finally { + fs.rmSync(tempRoot, { recursive: true, force: true }); + } +} else { + runOxfmt(files); + changed.push(...repairFiles(ROOT, files)); +} + +if (CHECK && changed.length > 0) { + console.error(`Format issues found in ${changed.length} docs file(s):`); + for (const relativePath of changed) { + console.error(`- ${relativePath}`); + } + process.exit(1); +} + +if (changed.length > 0) { + console.log(`Formatted ${changed.length} docs file(s).`); +} else { + console.log(`Docs formatting clean (${files.length} files).`); +}