| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- import { readFile, writeFile, readdir } from 'fs/promises';
- import path from 'path';
- import { fileURLToPath } from 'url';
- import { nanoid } from 'nanoid'
- import { LibraryItem_v1, LibraryItem, LibraryItems } from '@excalidraw/excalidraw/types'
- // 合并素材库
- // 由于官方不支持多素材库文件,只能将其合并成一个文件
- type LibraryItems_v1 = readonly LibraryItem_v1[];
- interface ImportedLibraryData extends Partial<ExportedLibraryData> {
- /** @compatible v1 */
- library?: LibraryItems;
- }
- interface ExportedLibraryData {
- type: string;
- version: number;
- source: string;
- libraryItems: LibraryItems;
- }
- /**
- * checks if library item does not exist already in current library
- */
- const isUniqueItem = (
- existingLibraryItems: LibraryItems,
- targetLibraryItem: LibraryItem,
- ) => {
- return !existingLibraryItems.find((libraryItem) => {
- if (libraryItem.elements.length !== targetLibraryItem.elements.length) {
- return false;
- }
- // detect z-index difference by checking the excalidraw elements
- // are in order
- return libraryItem.elements.every((libItemExcalidrawItem, idx) => {
- return (
- libItemExcalidrawItem.id === targetLibraryItem.elements[idx].id &&
- libItemExcalidrawItem.versionNonce ===
- targetLibraryItem.elements[idx].versionNonce
- );
- });
- });
- };
- const isUniqueItemV1 = (
- existingLibraryItems: LibraryItems,
- targetLibraryItem: LibraryItem_v1,
- ) => {
- return !existingLibraryItems.find((libraryItem) => {
- if (libraryItem.elements.length !== targetLibraryItem.length) {
- return false;
- }
- // detect z-index difference by checking the excalidraw elements
- // are in order
- return libraryItem.elements.every((libItemExcalidrawItem, idx) => {
- return (
- libItemExcalidrawItem.id === targetLibraryItem[idx].id &&
- libItemExcalidrawItem.versionNonce ===
- targetLibraryItem[idx].versionNonce
- );
- });
- });
- };
- /** Merges otherItems into localItems. Unique items in otherItems array are
- sorted first. */
- const mergeLibraryItems = (
- localItems: LibraryItems,
- otherItems: LibraryItems | LibraryItems_v1,
- version: number
- ): LibraryItems => {
- const newItems: LibraryItem[] = [];
- otherItems.map(item => {
- if (version === 1 && isUniqueItemV1(localItems, item)) {
- newItems.push({
- status: "published",
- elements: [...item],
- id: nanoid(),
- created: Date.now()
- });
- } else if (isUniqueItem(localItems, item)) {
- newItems.push({...item});
- }
- })
- return [...newItems, ...localItems];
- };
- const __filename = fileURLToPath(import.meta.url);
- const __dirname = path.dirname(__filename);
- const inputDir = path.resolve(__dirname, '../lib'); // 输入目录
- const outputFile = path.resolve(__dirname, '../library.excalidrawlib'); // 输出文件路径
- function isExcalidrawlibFile(fileName: string): boolean {
- return fileName.endsWith('.excalidrawlib');
- }
- function positionError(obj: any, msg?: string): false {
- console.error({
- error_msg: msg,
- error_obj: obj
- })
- return false
- }
- function isLibraryItem(obj: any): obj is LibraryItem {
- if (!obj) return positionError(obj, "LibraryItem obj does not exist")
- if (!obj.id || !obj.status || !obj.elements || !obj.created) return positionError(obj, "obj missing fields")
- if (typeof obj.id !== 'string') return positionError(obj, "obj.id type is not string")
- if (obj.status !== "published" && obj.status !== "unpublished") return positionError(obj, "obj.status value is wrong")
- if (!Array.isArray(obj.elements)) return positionError(obj, "obj.elements is not an array")
- if (typeof obj.created !== 'number') return positionError(obj, "obj.created type is not number")
- return true
- }
- function isLibrary(obj: any): obj is LibraryItem {
- if (!obj) return positionError(obj, "Library obj does not exist")
- if (!Array.isArray(obj)) return positionError(obj, "obj.elements is not an array")
- return true
- }
- function isLibraryItems(obj: any): obj is LibraryItems {
- if (!obj) return positionError(obj, "LibraryItems obj does not exist")
- if (!Array.isArray(obj)) return positionError(obj, "obj is not an array")
- for (const item of obj) {
- if (!isLibraryItem(item)) return positionError(obj, "LibraryItems is not an array")
- }
- return true
- }
- function isImportedLibraryData(obj: any): obj is ImportedLibraryData {
- if (!obj) return positionError(obj, "ImportedLibraryData obj does not exist")
- if (obj.type !== "excalidrawlib") return positionError(obj, "obj.type value is not excalidrawlib")
- if (typeof obj.version !== "number") return positionError(obj, "obj.version type is not number")
- if ((!obj.libraryItems || !isLibraryItems(obj.libraryItems))
- && (!obj.library || !isLibrary(obj.library))
- ) return positionError(obj, "The library array does not exist or is wrong")
- return true
- }
- async function readExcalidrawlibFile(filePath: string): Promise<ImportedLibraryData> {
- const content = await readFile(filePath, 'utf-8');
- const parsed = JSON.parse(content);
- if (!isImportedLibraryData(parsed)) {
- console.error({filePath, parsed})
- throw new Error(`Invalid excalidrawlib file: ${filePath}`);
- }
- return parsed;
- }
- async function mergeExcalidrawlibFiles(dir: string): Promise<ImportedLibraryData | undefined> {
- const files = (await readdir(dir)).filter(isExcalidrawlibFile);
- let libraryItems: LibraryItems = []
- let mianLibData: ExportedLibraryData | undefined = undefined
- for (const file of files) {
- const fullPath = path.join(dir, file);
- const data = await readExcalidrawlibFile(fullPath);
- if (file === "library.excalidrawlib") {
- mianLibData = {
- type: data.type ?? "excalidrawlib",
- version: data.version ?? 2,
- source: data.source ?? "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor",
- libraryItems: data.libraryItems ?? data.library ?? []
- }
- } else {
- libraryItems = mergeLibraryItems(libraryItems, data.libraryItems ?? data.library ?? [], data.version ?? 1)
- }
- }
- if (mianLibData === undefined) return undefined
- mianLibData.libraryItems = mergeLibraryItems(mianLibData.libraryItems, libraryItems)
- return mianLibData;
- }
- async function saveMergedExcalidrawlib(data: ImportedLibraryData, filePath: string) {
- const jsonString = JSON.stringify(data, null, 2); // 缩进美化
- await writeFile(filePath, jsonString, 'utf-8');
- console.log(`✅ 合并完成,已保存到 ${filePath}`);
- }
- // 执行流程
- const merged = await mergeExcalidrawlibFiles(inputDir);
- if (merged !== undefined) {
- saveMergedExcalidrawlib(merged, outputFile);
- }
|