You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Desktop, Cli: Fixes #6197: Fixed creation of empty notebooks when importing directory of files (#6274)
This commit is contained in:
		| @@ -1,21 +1,33 @@ | ||||
| import InteropService_Importer_Md from '../../services/interop/InteropService_Importer_Md'; | ||||
| import Note from '../../models/Note'; | ||||
| import { setupDatabaseAndSynchronizer, supportDir, switchClient } from '../../testing/test-utils'; | ||||
| 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'; | ||||
|  | ||||
|  | ||||
| describe('InteropService_Importer_Md: importLocalImages', function() { | ||||
| describe('InteropService_Importer_Md', function() { | ||||
| 	let tempDir: string; | ||||
| 	async function importNote(path: string) { | ||||
| 		const importer = new InteropService_Importer_Md(); | ||||
| 		importer.setMetadata({ fileExtensions: ['md', 'html'] }); | ||||
| 		return await importer.importFile(path, 'notebook'); | ||||
| 	} | ||||
|  | ||||
| 	async function importNoteDirectory(path: string) { | ||||
| 		const importer = new InteropService_Importer_Md(); | ||||
| 		importer.setMetadata({ fileExtensions: ['md', 'html'] }); | ||||
| 		return await importer.importDirectory(path, 'notebook'); | ||||
| 	} | ||||
| 	beforeEach(async (done) => { | ||||
| 		await setupDatabaseAndSynchronizer(1); | ||||
| 		await switchClient(1); | ||||
| 		tempDir = await createTempDir(); | ||||
| 		done(); | ||||
| 	}); | ||||
| 	afterEach(async () => { | ||||
| 		await fs.remove(tempDir); | ||||
| 	}); | ||||
| 	it('should import linked files and modify tags appropriately', async function() { | ||||
| 		const note = await importNote(`${supportDir}/test_notes/md/sample.md`); | ||||
|  | ||||
| @@ -117,4 +129,30 @@ describe('InteropService_Importer_Md: importLocalImages', function() { | ||||
| 		const preservedAlt = note.body.includes('alt="../../photo.jpg"'); | ||||
| 		expect(preservedAlt).toBe(true); | ||||
| 	}); | ||||
| 	it('should import non-empty directory', async function() { | ||||
| 		await fs.mkdirp(`${tempDir}/non-empty/non-empty`); | ||||
| 		await fs.writeFile(`${tempDir}/non-empty/non-empty/sample.md`, '# Sample'); | ||||
|  | ||||
| 		await importNoteDirectory(`${tempDir}/non-empty`); | ||||
| 		const allFolders = await Folder.all(); | ||||
| 		expect(allFolders.map((f: FolderEntity) => f.title).indexOf('non-empty')).toBeGreaterThanOrEqual(0); | ||||
| 	}); | ||||
| 	it('should not import empty directory', async function() { | ||||
| 		await fs.mkdirp(`${tempDir}/empty/empty`); | ||||
|  | ||||
| 		await importNoteDirectory(`${tempDir}/empty`); | ||||
| 		const allFolders = await Folder.all(); | ||||
| 		expect(allFolders.map((f: FolderEntity) => f.title).indexOf('empty')).toBe(-1); | ||||
| 	}); | ||||
| 	it('should import directory with non-empty subdirectory', async function() { | ||||
| 		await fs.mkdirp(`${tempDir}/non-empty-subdir/non-empty-subdir/subdir-empty`); | ||||
| 		await fs.mkdirp(`${tempDir}/non-empty-subdir/non-empty-subdir/subdir-non-empty`); | ||||
| 		await fs.writeFile(`${tempDir}/non-empty-subdir/non-empty-subdir/subdir-non-empty/sample.md`, '# Sample'); | ||||
|  | ||||
| 		await importNoteDirectory(`${tempDir}/non-empty-subdir`); | ||||
| 		const allFolders = await Folder.all(); | ||||
| 		expect(allFolders.map((f: FolderEntity) => f.title).indexOf('non-empty-subdir')).toBeGreaterThanOrEqual(0); | ||||
| 		expect(allFolders.map((f: FolderEntity) => f.title).indexOf('subdir-empty')).toBe(-1); | ||||
| 		expect(allFolders.map((f: FolderEntity) => f.title).indexOf('subdir-non-empty')).toBeGreaterThanOrEqual(0); | ||||
| 	}); | ||||
| }); | ||||
|   | ||||
| @@ -47,13 +47,16 @@ export default class InteropService_Importer_Md extends InteropService_Importer_ | ||||
|  | ||||
| 	async importDirectory(dirPath: string, parentFolderId: string) { | ||||
| 		console.info(`Import: ${dirPath}`); | ||||
|  | ||||
| 		const supportedFileExtension = this.metadata().fileExtensions; | ||||
| 		const stats = await shim.fsDriver().readDirStats(dirPath); | ||||
| 		for (let i = 0; i < stats.length; i++) { | ||||
| 			const stat = stats[i]; | ||||
|  | ||||
| 			if (stat.isDirectory()) { | ||||
| 				if (await this.isDirectoryEmpty(`${dirPath}/${stat.path}`)) { | ||||
| 					console.info(`Ignoring empty directory: ${stat.path}`); | ||||
| 					continue; | ||||
| 				} | ||||
| 				const folderTitle = await Folder.findUniqueItemTitle(basename(stat.path)); | ||||
| 				const folder = await Folder.save({ title: folderTitle, parent_id: parentFolderId }); | ||||
| 				await this.importDirectory(`${dirPath}/${basename(stat.path)}`, folder.id); | ||||
| @@ -63,6 +66,24 @@ export default class InteropService_Importer_Md extends InteropService_Importer_ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	private async isDirectoryEmpty(dirPath: string) { | ||||
| 		const supportedFileExtension = this.metadata().fileExtensions; | ||||
| 		const innerStats = await shim.fsDriver().readDirStats(dirPath); | ||||
| 		for (let i = 0; i < innerStats.length; i++) { | ||||
| 			const innerStat = innerStats[i]; | ||||
|  | ||||
| 			if (innerStat.isDirectory()) { | ||||
| 				if (!(await this.isDirectoryEmpty(`${dirPath}/${innerStat.path}`))) { | ||||
| 					return false; | ||||
| 				} | ||||
| 			} else if (supportedFileExtension.indexOf(fileExtension(innerStat.path).toLowerCase()) >= 0) { | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
| 		return true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	private trimAnchorLink(link: string) { | ||||
| 		if (link.indexOf('#') <= 0) return link; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user