一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
82 行
2.7 KiB
TypeScript
82 行
2.7 KiB
TypeScript
import { ImportExportResult, ImportOptions } from './types';
|
|
import importEnex, { restoreEnexNoteLinks } from '../../import-enex';
|
|
import InteropService_Importer_Base from './InteropService_Importer_Base';
|
|
import Folder from '../../models/Folder';
|
|
import { FolderEntity } from '../database/types';
|
|
import { fileExtension, rtrimSlashes } from '../../path-utils';
|
|
import shim from '../../shim';
|
|
import { filename } from '../../path-utils';
|
|
import Note from '../../models/Note';
|
|
|
|
const doImportEnex = async (destFolder: FolderEntity, sourcePath: string, options: ImportOptions) => {
|
|
if (!destFolder) {
|
|
const folderTitle = await Folder.findUniqueItemTitle(filename(sourcePath));
|
|
destFolder = await Folder.save({ title: folderTitle });
|
|
}
|
|
|
|
return await importEnex(destFolder.id, sourcePath, options);
|
|
};
|
|
|
|
const restoreCrossFolderLinks = async (noteIds: string[], importedFolderIds: string[], importOptions: ImportOptions) => {
|
|
const readNotes = async function*() {
|
|
for (const id of noteIds) {
|
|
const note = await Note.load(id, { fields: ['id', 'body'] });
|
|
yield { id: note.id, body: note.body };
|
|
}
|
|
};
|
|
const titleToIds = async (title: string) => {
|
|
const notes = await Note.allByTitleAndParent({ title, whereParentIn: importedFolderIds, fields: ['id'] });
|
|
return notes.map(n => n.id);
|
|
};
|
|
|
|
await restoreEnexNoteLinks(
|
|
readNotes(),
|
|
titleToIds,
|
|
importOptions,
|
|
);
|
|
};
|
|
|
|
export const enexImporterExec = async (result: ImportExportResult, destinationFolder: FolderEntity, sourcePath: string, fileExtensions: string[], options: ImportOptions) => {
|
|
sourcePath = rtrimSlashes(sourcePath);
|
|
|
|
|
|
if (await shim.fsDriver().isDirectory(sourcePath)) {
|
|
const notesWithUnresolvedLinks = [];
|
|
const importedFolderIds = new Set<string>();
|
|
|
|
const stats = await shim.fsDriver().readDirStats(sourcePath);
|
|
for (const stat of stats) {
|
|
const fullPath = `${sourcePath}/${stat.path}`;
|
|
if (!fileExtensions.includes(fileExtension(fullPath).toLowerCase())) continue;
|
|
|
|
try {
|
|
const importResult = await doImportEnex(null, fullPath, options);
|
|
notesWithUnresolvedLinks.push(...importResult.noteIdsWithUnresolvedLinks);
|
|
importedFolderIds.add(importResult.parentFolderId);
|
|
} catch (error) {
|
|
result.warnings.push(`When importing "${fullPath}": ${error.message}`);
|
|
}
|
|
}
|
|
|
|
await restoreCrossFolderLinks(notesWithUnresolvedLinks, [...importedFolderIds], options);
|
|
} else {
|
|
await doImportEnex(destinationFolder, sourcePath, options);
|
|
}
|
|
|
|
|
|
return result;
|
|
};
|
|
|
|
export default class InteropService_Importer_EnexToMd extends InteropService_Importer_Base {
|
|
public async exec(result: ImportExportResult) {
|
|
return enexImporterExec(
|
|
result,
|
|
this.options_.destinationFolder,
|
|
this.sourcePath_,
|
|
this.metadata().fileExtensions,
|
|
this.options_,
|
|
);
|
|
}
|
|
|
|
}
|