SPB Git

Repository Public Research preview

index.mjs 3.6 KB 67 lines javascript

1// Entry point of the audit engine: loads a manuscript from a confined path or2// from inline text, attaches its bibliography, and exposes every audit.3import { dirname, join } from "node:path";4import { AuditError, MAX_FILE_BYTES, readInside } from "./io.mjs";5import { detectFormat, parseBib, parseManuscript } from "./parse.mjs";67export { AuditError } from "./io.mjs";8export { parseManuscript, parseBib, detectFormat, countColumns } from "./parse.mjs";9export { outline, auditCitations, auditCrossReferences, auditNumbers, auditStatistics, auditTables, auditClaims, auditStyle, auditReplication, extractNumbers, ROLES, DISCLAIMER } from "./audits.mjs";10export { fullAudit, reviewScaffold, compareVersions, ENGINE_VERSION } from "./report.mjs";11export { CODES } from "./codes.mjs";1213/**14 * Loads a manuscript. Exactly one of `path` (inside `root`) or `text` is required.15 * With a path, \input / \include files and declared bibliography files are read16 * from the same root. With inline text nothing is read from disk.17 */18export function loadDocument({ root = process.cwd(), path, text, format, bib_path, bib_text } = {}) {19  const hasPath = typeof path === "string" && path.length > 0;20  const hasText = typeof text === "string";21  if (hasPath === hasText) throw new AuditError("input_invalid", "Provide exactly one of `path` (a file inside the allowed root) or `text` (the manuscript source).");22  if (format && !["auto", "latex", "markdown"].includes(format)) throw new AuditError("input_invalid", "`format` must be auto, latex or markdown.");23  if (bib_path && bib_text) throw new AuditError("input_invalid", "Provide `bib_path` or `bib_text`, not both.");24  let doc;25  const bibFiles = [], missing = [];26  let entries = [], problems = [];27  const addBib = (file, content) => { const r = parseBib(content, file); bibFiles.push(file); entries = entries.concat(r.entries); problems = problems.concat(r.problems); };28  if (hasText) {29    if (Buffer.byteLength(text) > MAX_FILE_BYTES) throw new AuditError("file_too_large", `Inline text is larger than ${MAX_FILE_BYTES} bytes.`);30    doc = parseManuscript({ file: "<inline>", text, format: format && format !== "auto" ? format : detectFormat("", text) });31    if (bib_path) throw new AuditError("input_invalid", "`bib_path` needs `path`. With inline text, pass the bibliography as `bib_text`.");32  } else {33    const main = readInside(root, path);34    const base = dirname(main.rel);35    const reader = (name) => {36      for (const cand of /\.[A-Za-z0-9]+$/.test(name) ? [name] : [`${name}.tex`, name]) {37        try {38          const r = readInside(root, join(base, cand));39          return { file: r.rel, text: r.text };40        } catch (e) {41          if (e.code === "path_forbidden") return { reason: "outside the allowed root" };42          if (e.code !== "not_found" && e.code !== "not_a_file") return { reason: e.code ?? "unreadable" };43        }44      }45      return { reason: "not found" };46    };47    doc = parseManuscript({ file: main.rel, text: main.text, format: format && format !== "auto" ? format : detectFormat(main.rel, main.text), reader });48    if (bib_path) {49      const b = readInside(root, bib_path);50      addBib(b.rel, b.text);51    } else if (!bib_text) {52      for (const name of [...new Set(doc.bibSources)]) {53        const cand = /\.(bib|json)$/i.test(name) ? name : `${name}.bib`;54        try {55          const b = readInside(root, join(base, cand));56          addBib(b.rel, b.text);57        } catch {58          missing.push(cand);59        }60      }61    }62  }63  if (bib_text) addBib("<inline-bib>", bib_text);64  if (bibFiles.length || missing.length) doc.bib = { files: bibFiles, entries, problems, missingFiles: missing };65  return doc;66}67
Files main
Menu

Theme