From 9cf830b0c06cd394207f6c31f7cd8c61e591ab28 Mon Sep 17 00:00:00 2001 From: aldrinjenson Date: Thu, 12 Mar 2026 11:29:18 -0400 Subject: [PATCH] fix: handle null declaration in DocxExporter to prevent export crash When exporting a SuperDoc to DOCX, if the converter's declaration is null (e.g., for documents created/edited via Yjs collaboration without raw XML import), the export crashes with: TypeError: Cannot read properties of null (reading 'attributes'). Add optional chaining and a standard XML declaration fallback (version="1.0" encoding="UTF-8" standalone="yes") so export succeeds when converter.declaration is null or undefined. Co-Authored-By: Claude Opus 4.6 --- .../src/core/super-converter/exporter.js | 6 ++- .../src/tests/export/docxExporter.test.js | 42 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/super-editor/src/core/super-converter/exporter.js b/packages/super-editor/src/core/super-converter/exporter.js index c13c45afe8..ef5288efbb 100644 --- a/packages/super-editor/src/core/super-converter/exporter.js +++ b/packages/super-editor/src/core/super-converter/exporter.js @@ -577,7 +577,11 @@ export class DocxExporter { #generate_xml_as_list(data, debug = false) { const json = JSON.parse(JSON.stringify(data)); - const declaration = this.converter.declaration.attributes; + const declaration = this.converter.declaration?.attributes ?? { + version: '1.0', + encoding: 'UTF-8', + standalone: 'yes', + }; const xmlTag = ` ` ${key}="${value}"`) .join('')}?>`; diff --git a/packages/super-editor/src/tests/export/docxExporter.test.js b/packages/super-editor/src/tests/export/docxExporter.test.js index cdc779f07e..86e222670b 100644 --- a/packages/super-editor/src/tests/export/docxExporter.test.js +++ b/packages/super-editor/src/tests/export/docxExporter.test.js @@ -41,6 +41,48 @@ describe('DocxExporter', () => { expect(xml).toContain('Format=<<NUM>>_<<VER>>'); }); + describe('null declaration handling', () => { + it('uses default XML declaration when converter.declaration is null', () => { + const exporter = new DocxExporter({ declaration: null }); + + const data = { + name: 'w:document', + attributes: {}, + elements: [ + { + name: 'w:t', + elements: [{ type: 'text', text: 'Hello' }], + }, + ], + }; + + const xml = exporter.schemaToXml(data); + + expect(xml).toContain(''); + expect(xml).toContain('Hello'); + }); + + it('uses default XML declaration when converter.declaration is undefined', () => { + const exporter = new DocxExporter({}); + + const data = { + name: 'w:document', + attributes: {}, + elements: [ + { + name: 'w:t', + elements: [{ type: 'text', text: 'Hello' }], + }, + ], + }; + + const xml = exporter.schemaToXml(data); + + expect(xml).toContain(''); + expect(xml).toContain('Hello'); + }); + }); + it('encodes all ampersands in text nodes including entity-like sequences', () => { const exporter = new DocxExporter(createConverterStub());