From 4761cec5982cc8c344c165148481f9349ce94fd7 Mon Sep 17 00:00:00 2001 From: Mohammad Betamony Date: Thu, 11 Jun 2026 17:47:43 +0300 Subject: [PATCH 1/2] LEAN-5686 --- src/jats/__tests__/jats-exporter.test.ts | 39 +++++++++++++++++++++++- src/jats/exporter/jats-exporter.ts | 14 +++++++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/jats/__tests__/jats-exporter.test.ts b/src/jats/__tests__/jats-exporter.test.ts index b5123d73..4c2e1225 100644 --- a/src/jats/__tests__/jats-exporter.test.ts +++ b/src/jats/__tests__/jats-exporter.test.ts @@ -22,7 +22,11 @@ import { parseJATSArticle } from '../importer/parse-jats-article' import { DEFAULT_CSL_OPTIONS } from './citations' import { sectionCategories } from './data/section-categories' import { readAndParseFixture } from './files' -import { BibliographyItemNode, isBibliographyItemNode } from '../../schema' +import { + BibliographyItemNode, + isBibliographyItemNode, + isCitationNode, +} from '../../schema' const parseXMLWithDTD = (data: string) => parseXml(data, { @@ -197,6 +201,39 @@ describe('JATS exporter', () => { ) }) + test('export uncited references when includeUncitedReferences is set', async () => { + const input = await readAndParseFixture('jats-document.xml') + const node = parseJATSArticle(input, sectionCategories) + + const bibliographyItemIDs = new Set() + const citedIDs = new Set() + node.descendants((n) => { + if (isBibliographyItemNode(n)) { + bibliographyItemIDs.add(n.attrs.id) + } + if (isCitationNode(n)) { + n.attrs.rids.forEach((rid: string) => citedIDs.add(rid)) + } + }) + + const withoutUncited = await new JATSExporter().serializeToJATS(node, { + csl: DEFAULT_CSL_OPTIONS, + }) + const withUncited = await new JATSExporter().serializeToJATS(node, { + csl: DEFAULT_CSL_OPTIONS, + includeUncitedReferences: true, + }) + + const countRefs = (xml: string) => + parseXMLWithDTD(xml).find('//ref-list/ref').length + + expect(countRefs(withoutUncited)).toBe(citedIDs.size) + expect(countRefs(withUncited)).toBe(bibliographyItemIDs.size) + + const { errors } = parseXMLWithDTD(withUncited) + expect(errors).toHaveLength(0) + }) + test('export footnotes', async () => { const transformer = new JATSExporter() const input = await readAndParseFixture('jats-fn-group.xml') diff --git a/src/jats/exporter/jats-exporter.ts b/src/jats/exporter/jats-exporter.ts index 7b9ac575..23b358c5 100644 --- a/src/jats/exporter/jats-exporter.ts +++ b/src/jats/exporter/jats-exporter.ts @@ -147,6 +147,7 @@ export type CSLOptions = { export type ExportOptions = { version?: Version csl: CSLOptions + includeUncitedReferences?: boolean } export class JATSExporter { @@ -191,7 +192,7 @@ export class JATSExporter { ): Promise => { this.manuscriptNode = manuscriptNode this.populateNodesMap() - this.initCiteprocEngine(options.csl) + this.initCiteprocEngine(options) this.createSerializer() const versionIds = selectVersionIds(options.version ?? '1.2') @@ -283,7 +284,8 @@ export class JATSExporter { element.appendChild(caption) } } - private initCiteprocEngine = (csl: CSLOptions) => { + private initCiteprocEngine = (options: ExportOptions) => { + const { includeUncitedReferences, csl } = options const bibitems: Map = new Map() const citations: Map = new Map() @@ -313,7 +315,13 @@ export class JATSExporter { ) engine.setOutputFormat('jats') - const output = engine.rebuildProcessorState([...citations.values()]) + const uncitedItemIDs = includeUncitedReferences ? [...bibitems.keys()] : [] + + const output = engine.rebuildProcessorState( + [...citations.values()], + undefined, + uncitedItemIDs + ) this.engine = engine this.renderedCitations = new Map(output.map((i) => [i[0], i[2]])) From 18579d587572855f7171cd42ee3cba7b766a8a6a Mon Sep 17 00:00:00 2001 From: asouqi Date: Thu, 11 Jun 2026 22:26:07 +0300 Subject: [PATCH 2/2] lean-prod - Opening previously created Drafts results in "Document not found" error in Editor for some items (LEAN-5747) --- src/schema/__tests__/docs.ts | 40 +++++++++++++++++++ .../migration/migration-scripts/4.3.34.ts | 32 +++++++++------ 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/schema/__tests__/docs.ts b/src/schema/__tests__/docs.ts index acb363af..8cd66b01 100644 --- a/src/schema/__tests__/docs.ts +++ b/src/schema/__tests__/docs.ts @@ -653,6 +653,46 @@ export const v2_3_20 = { }, ], }, + { + type: 'figure_element', + attrs: { + id: 'MPFigureElement:ca6dac1a-b6ae-4227-be6e-75119186224b', + label: '', + sizeFraction: 0, + suppressCaption: true, + suppressTitle: true, + dataTracked: null, + }, + content: [ + { + type: 'figure', + attrs: { + id: 'MPFigure:d377ead2-6849-4597-9caa-f3e6b73bb629', + src: 'chemoji/chemoji8.png', + contentType: 'image/png', + dataTracked: null, + }, + }, + { + type: 'figcaption', + attrs: { + dataTracked: null, + }, + }, + { + type: 'listing', + attrs: { + id: '', + contents: '', + language: '', + languageKey: 'null', + isExpanded: false, + isExecuting: false, + dataTracked: null, + }, + }, + ], + }, { type: 'table_element', attrs: { diff --git a/src/schema/migration/migration-scripts/4.3.34.ts b/src/schema/migration/migration-scripts/4.3.34.ts index fcbb557a..712ba2d7 100644 --- a/src/schema/migration/migration-scripts/4.3.34.ts +++ b/src/schema/migration/migration-scripts/4.3.34.ts @@ -59,18 +59,26 @@ class Migration4334 implements MigrationScript { const foundTitle = (figCaption?.content || []).find( (n) => n.type === 'caption_title' ) - const foundCaption = (figCaption?.content || []).filter( - (node) => node.type === 'caption') - .reduce( - (caption, { content }) => ({ - ...caption, - content: [ - ...(caption.content || []), - { type: 'text_block', content, attrs: {} }, - ], - }), - { type: 'caption', content: [], attrs: {} } - ) + const captionNodes = (figCaption?.content || []).filter( + (node) => node.type === 'caption' + ) + const foundCaption = + captionNodes.length === 0 + ? { + type: 'caption', + content: [{ type: 'text_block', attrs: {} }], + attrs: {}, + } + : captionNodes.reduce( + (caption, { content }) => ({ + ...caption, + content: [ + ...(caption.content || []), + { type: 'text_block', content, attrs: {} }, + ], + }), + { type: 'caption', content: [], attrs: {} } + ) const cleanContent = node.content.filter((n) => n.type !== 'figcaption')