diff --git a/packages/app-cli/tests/support/test_notes/md/invalid-image-link.md b/packages/app-cli/tests/support/test_notes/md/invalid-image-link.md new file mode 100644 index 000000000..ac44bf788 --- /dev/null +++ b/packages/app-cli/tests/support/test_notes/md/invalid-image-link.md @@ -0,0 +1 @@ +![sample](./invalid-image.jpg) diff --git a/packages/app-cli/tests/support/test_notes/md/invalid-image.jpg b/packages/app-cli/tests/support/test_notes/md/invalid-image.jpg new file mode 100644 index 000000000..824a4b76f --- /dev/null +++ b/packages/app-cli/tests/support/test_notes/md/invalid-image.jpg @@ -0,0 +1 @@ +![sample](./sample-no-links.md) diff --git a/packages/lib/services/interop/InteropService_Importer_Md.test.ts b/packages/lib/services/interop/InteropService_Importer_Md.test.ts index 5a97f0084..e6fde6685 100644 --- a/packages/lib/services/interop/InteropService_Importer_Md.test.ts +++ b/packages/lib/services/interop/InteropService_Importer_Md.test.ts @@ -4,7 +4,8 @@ import Folder from '../../models/Folder'; import * as fs from 'fs-extra'; import { createTempDir, setupDatabaseAndSynchronizer, supportDir, switchClient } from '../../testing/test-utils'; import { MarkupToHtml } from '@joplin/renderer'; -import { FolderEntity } from '../database/types'; +import { FolderEntity, NoteEntity, ResourceEntity } from '../database/types'; +import Resource from '../../models/Resource'; describe('InteropService_Importer_Md', () => { @@ -21,7 +22,7 @@ describe('InteropService_Importer_Md', () => { }); importer.setMetadata({ fileExtensions: ['md'] }); await importer.exec({ warnings: [] }); - const allNotes = await Note.all(); + const allNotes: NoteEntity[] = await Note.all(); return allNotes[0]; } async function importNoteDirectory(path: string) { @@ -184,4 +185,14 @@ describe('InteropService_Importer_Md', () => { expect(noteBeingReferenced.parent_id).toBe(targetFolder.id); }); + + it('should not fail to import file that contains a link to a file that does not exist', async () => { + // The first implicit test is that the below call doesn't throw due to the invalid image + const note = await importNote(`${supportDir}/test_notes/md/invalid-image-link.md`); + const links = Note.linkedItemIds(note.body); + expect(links.length).toBe(1); + const resource: ResourceEntity = await Resource.load(links[0]); + // The invalid image is imported as-is + expect(resource.title).toBe('invalid-image.jpg'); + }); }); diff --git a/packages/lib/services/interop/InteropService_Importer_Md.ts b/packages/lib/services/interop/InteropService_Importer_Md.ts index eebdca549..92fb9af26 100644 --- a/packages/lib/services/interop/InteropService_Importer_Md.ts +++ b/packages/lib/services/interop/InteropService_Importer_Md.ts @@ -134,7 +134,7 @@ export default class InteropService_Importer_Md extends InteropService_Importer_ id = this.importedNotes[resolvedPath].id; } else { - const resource = await shim.createResourceFromPath(pathWithExtension); + const resource = await shim.createResourceFromPath(pathWithExtension, null, { resizeLargeImages: 'never' }); id = resource.id; } diff --git a/packages/lib/shim-init-node.ts b/packages/lib/shim-init-node.ts index d21e226e3..38a3fb642 100644 --- a/packages/lib/shim-init-node.ts +++ b/packages/lib/shim-init-node.ts @@ -1,4 +1,4 @@ -import shim from './shim'; +import shim, { CreateResourceFromPathOptions } from './shim'; import GeolocationNode from './geolocation-node'; import { setLocale, defaultLocale, closestSupportedLocale } from './locale'; import FsDriverNode from './fs-driver-node'; @@ -8,6 +8,7 @@ import { basename, fileExtension, safeFileExtension } from './path-utils'; import * as fs from 'fs-extra'; import * as pdfJsNamespace from 'pdfjs-dist'; import { writeFile } from 'fs/promises'; +import { ResourceEntity } from './services/database/types'; const { FileApiDriverLocal } = require('./file-api-driver-local'); const mimeUtils = require('./mime-utils.js').mime; @@ -263,10 +264,13 @@ function shimInit(options: ShimInitOptions = null) { // from a file, and update one. To update a resource, pass the // destinationResourceId option. This method is indirectly tested in // Api.test.ts. - shim.createResourceFromPath = async function(filePath, defaultProps = null, options = null) { - options = { resizeLargeImages: 'always', // 'always', 'ask' or 'never' + shim.createResourceFromPath = async function(filePath, defaultProps: ResourceEntity = null, options: CreateResourceFromPathOptions = null) { + options = { + resizeLargeImages: 'always', // 'always', 'ask' or 'never' userSideValidation: false, - destinationResourceId: '', ...options }; + destinationResourceId: '', + ...options, + }; const readChunk = require('read-chunk'); const imageType = require('image-type'); diff --git a/packages/lib/shim.ts b/packages/lib/shim.ts index ac8f935c4..a7c97589d 100644 --- a/packages/lib/shim.ts +++ b/packages/lib/shim.ts @@ -2,6 +2,12 @@ import * as React from 'react'; import { NoteEntity, ResourceEntity } from './services/database/types'; import type FsDriverBase from './fs-driver-base'; +export interface CreateResourceFromPathOptions { + resizeLargeImages?: 'always' | 'never' | 'ask'; + userSideValidation?: boolean; + destinationResourceId?: string; +} + let isTestingEnv_ = false; // We need to ensure that there's only one instance of React being used by all @@ -202,7 +208,7 @@ const shim = { return r.text(); }, - createResourceFromPath: async (_filePath: string, _defaultProps: any = null, _options: any = null): Promise => { + createResourceFromPath: async (_filePath: string, _defaultProps: ResourceEntity = null, _options: CreateResourceFromPathOptions = null): Promise => { throw new Error('Not implemented'); },