From b1db308989e0a600b4a29b8ebb7fa357670889b1 Mon Sep 17 00:00:00 2001 From: pedr Date: Fri, 30 Aug 2024 10:57:13 -0300 Subject: [PATCH 1/8] wip adding new checks to md import --- .../interop/InteropService_Importer_Md.ts | 78 ++++++++++--------- packages/utils/url.ts | 8 ++ 2 files changed, 51 insertions(+), 35 deletions(-) diff --git a/packages/lib/services/interop/InteropService_Importer_Md.ts b/packages/lib/services/interop/InteropService_Importer_Md.ts index 22ef8cf1f0a..c99968fa7bb 100644 --- a/packages/lib/services/interop/InteropService_Importer_Md.ts +++ b/packages/lib/services/interop/InteropService_Importer_Md.ts @@ -12,7 +12,7 @@ import htmlUtils from '../../htmlUtils'; import { unique } from '../../ArrayUtils'; const { pregQuote } = require('../../string-utils-common'); import { MarkupToHtml } from '@joplin/renderer'; -import { isDataUrl } from '@joplin/utils/url'; +import { isDataUrl, isFilenameTooLong, isMailTo } from '@joplin/utils/url'; import { stripBom } from '../../string-utils'; export default class InteropService_Importer_Md extends InteropService_Importer_Base { @@ -112,48 +112,56 @@ export default class InteropService_Importer_Md extends InteropService_Importer_ for (const encodedLink of fileLinks) { const link = decodeURI(encodedLink); + if (isMailTo(link)) continue; + if (isFilenameTooLong(link)) { + // If a link is too long + continue; + } + + if (isDataUrl(link)) { // Just leave it as it is. We could potentially import // it as a resource but for now that's good enough. - } else { - // Handle anchor links appropriately - const trimmedLink = this.trimAnchorLink(link); - const attachmentPath = filename(`${dirname(filePath)}/${trimmedLink}`, true); - const pathWithExtension = `${attachmentPath}.${fileExtension(trimmedLink)}`; - const stat = await shim.fsDriver().stat(pathWithExtension); - const isDir = stat ? stat.isDirectory() : false; - if (stat && !isDir) { - const supportedFileExtension = this.metadata().fileExtensions; - const resolvedPath = shim.fsDriver().resolve(pathWithExtension); - let id = ''; - // If the link looks like a note, then import it - if (supportedFileExtension.indexOf(fileExtension(trimmedLink).toLowerCase()) >= 0) { - // If the note hasn't been imported yet, do so now - if (!this.importedNotes[resolvedPath]) { - await this.importFile(resolvedPath, parentFolderId); - } - - id = this.importedNotes[resolvedPath].id; - } else { - const resource = await shim.createResourceFromPath(pathWithExtension, null, { resizeLargeImages: 'never' }); - id = resource.id; + continue; + } + + // Handle anchor links appropriately + const trimmedLink = this.trimAnchorLink(link); + const attachmentPath = filename(`${dirname(filePath)}/${trimmedLink}`, true); + const pathWithExtension = `${attachmentPath}.${fileExtension(trimmedLink)}`; + const stat = await shim.fsDriver().stat(pathWithExtension); + const isDir = stat ? stat.isDirectory() : false; + if (stat && !isDir) { + const supportedFileExtension = this.metadata().fileExtensions; + const resolvedPath = shim.fsDriver().resolve(pathWithExtension); + let id = ''; + // If the link looks like a note, then import it + if (supportedFileExtension.indexOf(fileExtension(trimmedLink).toLowerCase()) >= 0) { + // If the note hasn't been imported yet, do so now + if (!this.importedNotes[resolvedPath]) { + await this.importFile(resolvedPath, parentFolderId); } - // The first is a normal link, the second is supports the and []() syntax - // Only opening patterns are consider in order to cover all occurrences - // We need to use the encoded link as well because some links (link's with spaces) - // will appear encoded in the source. Other links (unicode chars) will not - const linksToReplace = [this.trimAnchorLink(link), this.trimAnchorLink(encodedLink)]; + id = this.importedNotes[resolvedPath].id; + } else { + const resource = await shim.createResourceFromPath(pathWithExtension, null, { resizeLargeImages: 'never' }); + id = resource.id; + } + + // The first is a normal link, the second is supports the and []() syntax + // Only opening patterns are consider in order to cover all occurrences + // We need to use the encoded link as well because some links (link's with spaces) + // will appear encoded in the source. Other links (unicode chars) will not + const linksToReplace = [this.trimAnchorLink(link), this.trimAnchorLink(encodedLink)]; - for (let j = 0; j < linksToReplace.length; j++) { - const linkToReplace = pregQuote(linksToReplace[j]); + for (let j = 0; j < linksToReplace.length; j++) { + const linkToReplace = pregQuote(linksToReplace[j]); - // Markdown links - updated = markdownUtils.replaceResourceUrl(updated, linkToReplace, id); + // Markdown links + updated = markdownUtils.replaceResourceUrl(updated, linkToReplace, id); - // HTML links - updated = htmlUtils.replaceResourceUrl(updated, linkToReplace, id); - } + // HTML links + updated = htmlUtils.replaceResourceUrl(updated, linkToReplace, id); } } } diff --git a/packages/utils/url.ts b/packages/utils/url.ts index 52987b4aaf5..b925b556378 100644 --- a/packages/utils/url.ts +++ b/packages/utils/url.ts @@ -101,6 +101,14 @@ export const isDataUrl = (path: string) => { return path.startsWith('data:'); }; +export const isMailTo = (path: string) => { + return path.startsWith('mailto:'); +}; + +export const isFilenameTooLong = (filename: string) => { + return filename.length > 255; +}; + export const hasProtocol = (url: string, protocol: string | string[]) => { if (!url) return false; From deceb7798f9f68de80dfc380700c00673b95a135 Mon Sep 17 00:00:00 2001 From: pedr Date: Fri, 30 Aug 2024 12:25:42 -0300 Subject: [PATCH 2/8] changing checks in favour of handling error --- .../lib/services/interop/InteropService_Importer_Md.ts | 9 +-------- packages/utils/url.ts | 8 -------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/lib/services/interop/InteropService_Importer_Md.ts b/packages/lib/services/interop/InteropService_Importer_Md.ts index c99968fa7bb..476cae98bc4 100644 --- a/packages/lib/services/interop/InteropService_Importer_Md.ts +++ b/packages/lib/services/interop/InteropService_Importer_Md.ts @@ -12,7 +12,7 @@ import htmlUtils from '../../htmlUtils'; import { unique } from '../../ArrayUtils'; const { pregQuote } = require('../../string-utils-common'); import { MarkupToHtml } from '@joplin/renderer'; -import { isDataUrl, isFilenameTooLong, isMailTo } from '@joplin/utils/url'; +import { isDataUrl } from '@joplin/utils/url'; import { stripBom } from '../../string-utils'; export default class InteropService_Importer_Md extends InteropService_Importer_Base { @@ -112,13 +112,6 @@ export default class InteropService_Importer_Md extends InteropService_Importer_ for (const encodedLink of fileLinks) { const link = decodeURI(encodedLink); - if (isMailTo(link)) continue; - if (isFilenameTooLong(link)) { - // If a link is too long - continue; - } - - if (isDataUrl(link)) { // Just leave it as it is. We could potentially import // it as a resource but for now that's good enough. diff --git a/packages/utils/url.ts b/packages/utils/url.ts index b925b556378..52987b4aaf5 100644 --- a/packages/utils/url.ts +++ b/packages/utils/url.ts @@ -101,14 +101,6 @@ export const isDataUrl = (path: string) => { return path.startsWith('data:'); }; -export const isMailTo = (path: string) => { - return path.startsWith('mailto:'); -}; - -export const isFilenameTooLong = (filename: string) => { - return filename.length > 255; -}; - export const hasProtocol = (url: string, protocol: string | string[]) => { if (!url) return false; From 7d25c00f2ccdefef2a3f06775f6d1e46cda23504 Mon Sep 17 00:00:00 2001 From: pedr Date: Fri, 30 Aug 2024 12:31:08 -0300 Subject: [PATCH 3/8] handle error on node fsDriver stat --- packages/lib/fs-driver-node.ts | 1 + packages/lib/fsDriver.test.ts | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/packages/lib/fs-driver-node.ts b/packages/lib/fs-driver-node.ts index 2807af1a5b6..d33987ce81c 100644 --- a/packages/lib/fs-driver-node.ts +++ b/packages/lib/fs-driver-node.ts @@ -97,6 +97,7 @@ export default class FsDriverNode extends FsDriverBase { }; } catch (error) { if (error.code === 'ENOENT') return null; + if (error.code === 'ENAMETOOLONG') return null; throw error; } } diff --git a/packages/lib/fsDriver.test.ts b/packages/lib/fsDriver.test.ts index 5984679ed03..769a805f8d6 100644 --- a/packages/lib/fsDriver.test.ts +++ b/packages/lib/fsDriver.test.ts @@ -42,4 +42,11 @@ describe('fsDriver', () => { await shim.fsDriver().findUniqueFilename(join(supportDir, 'this-file-does-not-exist.txt'), [join(supportDir, 'some-other-file.txt')]), ).toBe(join(supportDir, 'this-file-does-not-exist.txt')); }); + + it('should handle cases where filename is too long in stat', async () => { + const fsDriver = new FsDriverNode(); + const filename = `${'12345678'.repeat(32)}9`; + + expect(await fsDriver.stat(`./${filename}`)).toBe(null); + }); }); From f82c6d320a51d5fa0ba03352de82b77db85854ae Mon Sep 17 00:00:00 2001 From: pedr Date: Fri, 30 Aug 2024 15:20:01 -0300 Subject: [PATCH 4/8] reverting changes --- .../interop/InteropService_Importer_Md.ts | 69 +++++++++---------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/packages/lib/services/interop/InteropService_Importer_Md.ts b/packages/lib/services/interop/InteropService_Importer_Md.ts index 476cae98bc4..22ef8cf1f0a 100644 --- a/packages/lib/services/interop/InteropService_Importer_Md.ts +++ b/packages/lib/services/interop/InteropService_Importer_Md.ts @@ -115,46 +115,45 @@ export default class InteropService_Importer_Md extends InteropService_Importer_ if (isDataUrl(link)) { // Just leave it as it is. We could potentially import // it as a resource but for now that's good enough. - continue; - } - - // Handle anchor links appropriately - const trimmedLink = this.trimAnchorLink(link); - const attachmentPath = filename(`${dirname(filePath)}/${trimmedLink}`, true); - const pathWithExtension = `${attachmentPath}.${fileExtension(trimmedLink)}`; - const stat = await shim.fsDriver().stat(pathWithExtension); - const isDir = stat ? stat.isDirectory() : false; - if (stat && !isDir) { - const supportedFileExtension = this.metadata().fileExtensions; - const resolvedPath = shim.fsDriver().resolve(pathWithExtension); - let id = ''; - // If the link looks like a note, then import it - if (supportedFileExtension.indexOf(fileExtension(trimmedLink).toLowerCase()) >= 0) { - // If the note hasn't been imported yet, do so now - if (!this.importedNotes[resolvedPath]) { - await this.importFile(resolvedPath, parentFolderId); + } else { + // Handle anchor links appropriately + const trimmedLink = this.trimAnchorLink(link); + const attachmentPath = filename(`${dirname(filePath)}/${trimmedLink}`, true); + const pathWithExtension = `${attachmentPath}.${fileExtension(trimmedLink)}`; + const stat = await shim.fsDriver().stat(pathWithExtension); + const isDir = stat ? stat.isDirectory() : false; + if (stat && !isDir) { + const supportedFileExtension = this.metadata().fileExtensions; + const resolvedPath = shim.fsDriver().resolve(pathWithExtension); + let id = ''; + // If the link looks like a note, then import it + if (supportedFileExtension.indexOf(fileExtension(trimmedLink).toLowerCase()) >= 0) { + // If the note hasn't been imported yet, do so now + if (!this.importedNotes[resolvedPath]) { + await this.importFile(resolvedPath, parentFolderId); + } + + id = this.importedNotes[resolvedPath].id; + } else { + const resource = await shim.createResourceFromPath(pathWithExtension, null, { resizeLargeImages: 'never' }); + id = resource.id; } - id = this.importedNotes[resolvedPath].id; - } else { - const resource = await shim.createResourceFromPath(pathWithExtension, null, { resizeLargeImages: 'never' }); - id = resource.id; - } - - // The first is a normal link, the second is supports the and []() syntax - // Only opening patterns are consider in order to cover all occurrences - // We need to use the encoded link as well because some links (link's with spaces) - // will appear encoded in the source. Other links (unicode chars) will not - const linksToReplace = [this.trimAnchorLink(link), this.trimAnchorLink(encodedLink)]; + // The first is a normal link, the second is supports the and []() syntax + // Only opening patterns are consider in order to cover all occurrences + // We need to use the encoded link as well because some links (link's with spaces) + // will appear encoded in the source. Other links (unicode chars) will not + const linksToReplace = [this.trimAnchorLink(link), this.trimAnchorLink(encodedLink)]; - for (let j = 0; j < linksToReplace.length; j++) { - const linkToReplace = pregQuote(linksToReplace[j]); + for (let j = 0; j < linksToReplace.length; j++) { + const linkToReplace = pregQuote(linksToReplace[j]); - // Markdown links - updated = markdownUtils.replaceResourceUrl(updated, linkToReplace, id); + // Markdown links + updated = markdownUtils.replaceResourceUrl(updated, linkToReplace, id); - // HTML links - updated = htmlUtils.replaceResourceUrl(updated, linkToReplace, id); + // HTML links + updated = htmlUtils.replaceResourceUrl(updated, linkToReplace, id); + } } } } From f7cf69f4d9754ea8ec45f242cd7c9071f5828092 Mon Sep 17 00:00:00 2001 From: pedr Date: Mon, 2 Sep 2024 15:07:20 -0300 Subject: [PATCH 5/8] removing error handling from stat --- packages/lib/fs-driver-node.ts | 1 - packages/lib/fsDriver.test.ts | 7 ------- 2 files changed, 8 deletions(-) diff --git a/packages/lib/fs-driver-node.ts b/packages/lib/fs-driver-node.ts index d33987ce81c..2807af1a5b6 100644 --- a/packages/lib/fs-driver-node.ts +++ b/packages/lib/fs-driver-node.ts @@ -97,7 +97,6 @@ export default class FsDriverNode extends FsDriverBase { }; } catch (error) { if (error.code === 'ENOENT') return null; - if (error.code === 'ENAMETOOLONG') return null; throw error; } } diff --git a/packages/lib/fsDriver.test.ts b/packages/lib/fsDriver.test.ts index 769a805f8d6..5984679ed03 100644 --- a/packages/lib/fsDriver.test.ts +++ b/packages/lib/fsDriver.test.ts @@ -42,11 +42,4 @@ describe('fsDriver', () => { await shim.fsDriver().findUniqueFilename(join(supportDir, 'this-file-does-not-exist.txt'), [join(supportDir, 'some-other-file.txt')]), ).toBe(join(supportDir, 'this-file-does-not-exist.txt')); }); - - it('should handle cases where filename is too long in stat', async () => { - const fsDriver = new FsDriverNode(); - const filename = `${'12345678'.repeat(32)}9`; - - expect(await fsDriver.stat(`./${filename}`)).toBe(null); - }); }); From 0223773fd96774eb2bcd0e9aad3619cb711d3539 Mon Sep 17 00:00:00 2001 From: pedr Date: Mon, 2 Sep 2024 15:15:29 -0300 Subject: [PATCH 6/8] adding a new way to check if it is to a localfile --- .../InteropService_Importer_Md.test.ts | 23 +++++++++++++++++++ .../interop/InteropService_Importer_Md.ts | 16 +++++++++++++ packages/tools/cspell/dictionary2.txt | 1 + 3 files changed, 40 insertions(+) diff --git a/packages/lib/services/interop/InteropService_Importer_Md.test.ts b/packages/lib/services/interop/InteropService_Importer_Md.test.ts index e6fde6685c8..e37c4f9ad71 100644 --- a/packages/lib/services/interop/InteropService_Importer_Md.test.ts +++ b/packages/lib/services/interop/InteropService_Importer_Md.test.ts @@ -195,4 +195,27 @@ describe('InteropService_Importer_Md', () => { // The invalid image is imported as-is expect(resource.title).toBe('invalid-image.jpg'); }); + + it.each([ + 'https://example.com', + 'http://example.com', + 'https://example.com/image.png', + 'mailto:admin@example.com?subject=test', + 'onenote:Title of the note', + 'tel:554799992292910', + ])('should filter paths to external files', async (link: string) => { + const importer = new InteropService_Importer_Md(); + expect(importer.isLinkToLocalFile(link)).toBe(false); + }); + + it.each([ + 'asdfasf', + 'asdfasf.png', + 'base/path/asdfasf.png', + './base/path/asdfasf.png', + '/base/path/asdfasf.pdf', + ])('should consider local file', async (link: string) => { + const importer = new InteropService_Importer_Md(); + expect(importer.isLinkToLocalFile(link)).toBe(true); + }); }); diff --git a/packages/lib/services/interop/InteropService_Importer_Md.ts b/packages/lib/services/interop/InteropService_Importer_Md.ts index 22ef8cf1f0a..294d0cef676 100644 --- a/packages/lib/services/interop/InteropService_Importer_Md.ts +++ b/packages/lib/services/interop/InteropService_Importer_Md.ts @@ -112,6 +112,10 @@ export default class InteropService_Importer_Md extends InteropService_Importer_ for (const encodedLink of fileLinks) { const link = decodeURI(encodedLink); + // We could be importing data: as resources, but for now we are just + + if (!this.isLinkToLocalFile(link)) continue; + if (isDataUrl(link)) { // Just leave it as it is. We could potentially import // it as a resource but for now that's good enough. @@ -184,4 +188,16 @@ export default class InteropService_Importer_Md extends InteropService_Importer_ return this.importedNotes[resolvedPath]; } + + public isLinkToLocalFile(path: string) { + try { + const url = new URL(path); + if (url.protocol) return false; + } catch (error) { + // if it fails it probably is a relative path (local file) + if (error && error.code === 'ERR_INVALID_URL') return true; + throw error; + } + return false; + } } diff --git a/packages/tools/cspell/dictionary2.txt b/packages/tools/cspell/dictionary2.txt index 69700a2eddc..6e14faaf008 100644 --- a/packages/tools/cspell/dictionary2.txt +++ b/packages/tools/cspell/dictionary2.txt @@ -325,6 +325,7 @@ ondoctype onedrive onelink onend +onenote onfoo onformat onmatch From 07e7ff700bff9a651af3e995d59f06315cbb1488 Mon Sep 17 00:00:00 2001 From: pedr Date: Mon, 2 Sep 2024 15:17:21 -0300 Subject: [PATCH 7/8] simplifying importLocalFile and removing unused function --- .../interop/InteropService_Importer_Md.ts | 74 +++++++++---------- packages/utils/url.ts | 4 - 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/packages/lib/services/interop/InteropService_Importer_Md.ts b/packages/lib/services/interop/InteropService_Importer_Md.ts index 294d0cef676..de95c402450 100644 --- a/packages/lib/services/interop/InteropService_Importer_Md.ts +++ b/packages/lib/services/interop/InteropService_Importer_Md.ts @@ -12,7 +12,6 @@ import htmlUtils from '../../htmlUtils'; import { unique } from '../../ArrayUtils'; const { pregQuote } = require('../../string-utils-common'); import { MarkupToHtml } from '@joplin/renderer'; -import { isDataUrl } from '@joplin/utils/url'; import { stripBom } from '../../string-utils'; export default class InteropService_Importer_Md extends InteropService_Importer_Base { @@ -112,52 +111,47 @@ export default class InteropService_Importer_Md extends InteropService_Importer_ for (const encodedLink of fileLinks) { const link = decodeURI(encodedLink); - // We could be importing data: as resources, but for now we are just - + // Just leave it as it is. We could potentially import 'data:' links + // as a resource but for now that's good enough. if (!this.isLinkToLocalFile(link)) continue; - if (isDataUrl(link)) { - // Just leave it as it is. We could potentially import - // it as a resource but for now that's good enough. - } else { - // Handle anchor links appropriately - const trimmedLink = this.trimAnchorLink(link); - const attachmentPath = filename(`${dirname(filePath)}/${trimmedLink}`, true); - const pathWithExtension = `${attachmentPath}.${fileExtension(trimmedLink)}`; - const stat = await shim.fsDriver().stat(pathWithExtension); - const isDir = stat ? stat.isDirectory() : false; - if (stat && !isDir) { - const supportedFileExtension = this.metadata().fileExtensions; - const resolvedPath = shim.fsDriver().resolve(pathWithExtension); - let id = ''; - // If the link looks like a note, then import it - if (supportedFileExtension.indexOf(fileExtension(trimmedLink).toLowerCase()) >= 0) { - // If the note hasn't been imported yet, do so now - if (!this.importedNotes[resolvedPath]) { - await this.importFile(resolvedPath, parentFolderId); - } - - id = this.importedNotes[resolvedPath].id; - } else { - const resource = await shim.createResourceFromPath(pathWithExtension, null, { resizeLargeImages: 'never' }); - id = resource.id; + // Handle anchor links appropriately + const trimmedLink = this.trimAnchorLink(link); + const attachmentPath = filename(`${dirname(filePath)}/${trimmedLink}`, true); + const pathWithExtension = `${attachmentPath}.${fileExtension(trimmedLink)}`; + const stat = await shim.fsDriver().stat(pathWithExtension); + const isDir = stat ? stat.isDirectory() : false; + if (stat && !isDir) { + const supportedFileExtension = this.metadata().fileExtensions; + const resolvedPath = shim.fsDriver().resolve(pathWithExtension); + let id = ''; + // If the link looks like a note, then import it + if (supportedFileExtension.indexOf(fileExtension(trimmedLink).toLowerCase()) >= 0) { + // If the note hasn't been imported yet, do so now + if (!this.importedNotes[resolvedPath]) { + await this.importFile(resolvedPath, parentFolderId); } - // The first is a normal link, the second is supports the and []() syntax - // Only opening patterns are consider in order to cover all occurrences - // We need to use the encoded link as well because some links (link's with spaces) - // will appear encoded in the source. Other links (unicode chars) will not - const linksToReplace = [this.trimAnchorLink(link), this.trimAnchorLink(encodedLink)]; + id = this.importedNotes[resolvedPath].id; + } else { + const resource = await shim.createResourceFromPath(pathWithExtension, null, { resizeLargeImages: 'never' }); + id = resource.id; + } - for (let j = 0; j < linksToReplace.length; j++) { - const linkToReplace = pregQuote(linksToReplace[j]); + // The first is a normal link, the second is supports the and []() syntax + // Only opening patterns are consider in order to cover all occurrences + // We need to use the encoded link as well because some links (link's with spaces) + // will appear encoded in the source. Other links (unicode chars) will not + const linksToReplace = [this.trimAnchorLink(link), this.trimAnchorLink(encodedLink)]; - // Markdown links - updated = markdownUtils.replaceResourceUrl(updated, linkToReplace, id); + for (let j = 0; j < linksToReplace.length; j++) { + const linkToReplace = pregQuote(linksToReplace[j]); - // HTML links - updated = htmlUtils.replaceResourceUrl(updated, linkToReplace, id); - } + // Markdown links + updated = markdownUtils.replaceResourceUrl(updated, linkToReplace, id); + + // HTML links + updated = htmlUtils.replaceResourceUrl(updated, linkToReplace, id); } } } diff --git a/packages/utils/url.ts b/packages/utils/url.ts index 52987b4aaf5..fd08a80b510 100644 --- a/packages/utils/url.ts +++ b/packages/utils/url.ts @@ -97,10 +97,6 @@ export const fileUriToPath = (path: string, platform = 'linux') => { return output; }; -export const isDataUrl = (path: string) => { - return path.startsWith('data:'); -}; - export const hasProtocol = (url: string, protocol: string | string[]) => { if (!url) return false; From bf7e03b899002cafaa771d84cded9846a72bca37 Mon Sep 17 00:00:00 2001 From: pedr Date: Tue, 17 Sep 2024 15:09:20 -0300 Subject: [PATCH 8/8] remove repeated logic --- packages/lib/services/interop/InteropService_Importer_Md.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/lib/services/interop/InteropService_Importer_Md.ts b/packages/lib/services/interop/InteropService_Importer_Md.ts index de95c402450..d4136269b8d 100644 --- a/packages/lib/services/interop/InteropService_Importer_Md.ts +++ b/packages/lib/services/interop/InteropService_Importer_Md.ts @@ -185,8 +185,7 @@ export default class InteropService_Importer_Md extends InteropService_Importer_ public isLinkToLocalFile(path: string) { try { - const url = new URL(path); - if (url.protocol) return false; + new URL(path); } catch (error) { // if it fails it probably is a relative path (local file) if (error && error.code === 'ERR_INVALID_URL') return true;