diff --git a/packages/htmlpack/src/index.ts b/packages/htmlpack/src/index.ts index aa760b202..dfa45b706 100644 --- a/packages/htmlpack/src/index.ts +++ b/packages/htmlpack/src/index.ts @@ -33,8 +33,15 @@ const htmlentities = (s: string): string => { }; const dataUriEncode = (filePath: string): string => { - const result = Datauri(filePath); - return result.content; + try { + const result = Datauri(filePath); + return result.content; + } catch (error) { + // If the file path is invalid, the Datauri will throw an exception. + // Instead, since we can just ignore that particular file. + // Fixes https://github.com/laurent22/joplin/issues/8305 + return ''; + } }; const attributesHtml = (attr: any) => { diff --git a/packages/lib/services/interop/InteropService_Exporter_Html.test.ts b/packages/lib/services/interop/InteropService_Exporter_Html.test.ts index edfdecd31..0a80706ad 100644 --- a/packages/lib/services/interop/InteropService_Exporter_Html.test.ts +++ b/packages/lib/services/interop/InteropService_Exporter_Html.test.ts @@ -7,6 +7,7 @@ import * as fs from 'fs-extra'; import { tempFilePath } from '../../testing/test-utils'; import { ContentScriptType } from '../../services/plugins/api/types'; import { FileSystemItem } from './types'; +import { readFile } from 'fs/promises'; async function recreateExportDir() { const dir = exportDir(); @@ -120,4 +121,22 @@ describe('interop/InteropService_Exporter_Html', () => { expect(readFenceContent).toBe(fenceContent); })); + test('should not throw an error on invalid resource paths', (async () => { + const service = InteropService.instance(); + const folder1 = await Folder.save({ title: 'folder1' }); + await Note.save({ title: 'note1', parent_id: folder1.id, body: '[a link starts with slash](/)' }); + + const filePath = `${exportDir()}/test.html`; + + await service.export({ + path: filePath, + format: 'html', + packIntoSingleFile: true, + target: FileSystemItem.File, + }); + + const content = await readFile(filePath, 'utf-8'); + expect(content).toContain('a link starts with slash'); + })); + }); diff --git a/packages/lib/services/interop/InteropService_Exporter_Html.ts b/packages/lib/services/interop/InteropService_Exporter_Html.ts index 75f13af52..45a96694c 100644 --- a/packages/lib/services/interop/InteropService_Exporter_Html.ts +++ b/packages/lib/services/interop/InteropService_Exporter_Html.ts @@ -6,7 +6,7 @@ import Folder from '../../models/Folder'; import Note from '../../models/Note'; import Setting from '../../models/Setting'; import { MarkupToHtml } from '@joplin/renderer'; -import { ResourceEntity } from '../database/types'; +import { NoteEntity, ResourceEntity } from '../database/types'; import { contentScriptsToRendererRules } from '../plugins/utils/loadContentScripts'; import { basename, friendlySafeFilename, rtrimSlashes, dirname } from '../../path-utils'; import htmlpack from '@joplin/htmlpack'; @@ -48,7 +48,7 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte this.style_ = themeStyle(Setting.THEME_LIGHT); } - private async makeDirPath_(item: any, pathPart: string = null) { + private async makeDirPath_(item: NoteEntity, pathPart: string = null) { let output = ''; while (true) { if (item.type_ === BaseModel.TYPE_FOLDER) { @@ -64,7 +64,7 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte } } - private async processNoteResources_(item: any) { + private async processNoteResources_(item: NoteEntity) { const target = this.metadata().target; const linkedResourceIds = await Note.linkedResourceIds(item.body); const relativePath = target === 'directory' ? rtrimSlashes(await this.makeDirPath_(item, '..')) : '';