Path: blob/main/src/format/jats/format-jats-postprocess.ts
6453 views
/*1* format-jats-postprocess.ts2*3* Copyright (C) 2020-2022 Posit Software, PBC4*/56import { reformat } from "../../core/xml.ts";7import { RenderServices } from "../../command/render/types.ts";8import { JatsRenderSubArticle, xmlPlaceholder } from "./format-jats-types.ts";910import { dirname, join, relative } from "../../deno_ral/path.ts";11import { copySync } from "../../deno_ral/fs.ts";12import { readLines } from "io/read-lines";13import { ProjectContext } from "../../project/types.ts";14import { logProgress } from "../../core/log.ts";15import { kJatsSubarticle } from "../../render/notebook/notebook-types.ts";16import { Format } from "../../config/types.ts";1718// XML Linting19export const reformatXmlPostProcessor = async (output: string) => {20await reformat(output);21};2223// Injects the root subarticle24export const renderSubarticlePostProcessor = (25input: string,26format: Format,27subArticles: JatsRenderSubArticle[],28services: RenderServices,29project: ProjectContext,30quiet?: boolean,31) => {32return async (output: string) => {33// First ensure that we have rendered jats-subarticles34// for each of our articles. If needed, render any that35// aren't already rendered.36const subArticlesToRender = subArticles.filter((subArticle) => {37return services.notebook.get(subArticle.input, project) === undefined;38});39const total = subArticlesToRender.length;40if (subArticlesToRender.length > 0 && !quiet) {41logProgress("Rendering JATS sub-articles");42}4344// Do the rendering45let count = 0;46for (const subArticle of subArticlesToRender) {47const subArticlePath = subArticle.input;48const nbRelPath = relative(dirname(input), subArticlePath);49if (!quiet) {50logProgress(`[${++count}/${total}] ${nbRelPath}`);51}52await services.notebook.render(53subArticlePath,54format,55kJatsSubarticle,56services,57undefined,58project,59);60}6162// Go through the subarticles, embed them into the article63const supportingOut: string[] = [];64for (const subArticle of subArticles) {65const nb = services.notebook.get(subArticle.input, project);66if (nb && nb[kJatsSubarticle]) {67let outputContents = Deno.readTextFileSync(output);6869const notebook = nb[kJatsSubarticle];70const jatsSubarticlePath = notebook.path;71const placeholder = xmlPlaceholder(72subArticle.token,73subArticle.input,74);7576// Read the subarticle and change any cross references to not conflict77// by atdding a token suffix78const subArtReader = await Deno.open(jatsSubarticlePath);79const subArtLines: string[] = [];80for await (let line of readLines(subArtReader)) {81// Process ids (add a suffix to all ids and rids)82line = line.replaceAll(kIdRegex, `$1id="$2-${subArticle.token}"`);83line = line.replaceAll(kRidRegex, `$1rid="$2-${subArticle.token}"`);84subArtLines.push(line);85}8687// Replace the placeholder with the rendered subarticle88outputContents = outputContents.replaceAll(89placeholder,90() => subArtLines.join("\n"),91);9293// Move supporting and resource files into place94for (const support of notebook.supporting) {95// get the supporting relative path96const basePath = project ? project.dir : dirname(notebook.path);97const fromPath = join(basePath, support);98const toPath = join(99dirname(output),100relative(dirname(notebook.path), fromPath),101);102if (fromPath !== toPath) {103copySync(fromPath, toPath, { overwrite: true });104}105supportingOut.push(toPath);106}107Deno.writeTextFileSync(output, outputContents);108}109}110// Clean up the renderings now that they've been ingested111for (const subArticle of subArticles) {112services.notebook.removeRendering(113subArticle.input,114kJatsSubarticle,115supportingOut,116);117}118return {119supporting: supportingOut,120};121};122};123124const kIdRegex = /(\s+)id="([^"]*)"/g;125const kRidRegex = /(\s+)rid="([^"]*)"/g;126127128