update-excalidrawlib.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import { readFile, writeFile } from 'fs/promises';
  2. import path from 'path';
  3. import { fileURLToPath } from 'url';
  4. import { LibraryItem, LibraryItems } from '@excalidraw/excalidraw/types'
  5. // 如果新增了自定义素材,则需要运行这个脚本将素材同步到lib文件夹,以便下次合成素材库时,不会清除新增素材
  6. const __filename = fileURLToPath(import.meta.url);
  7. const __dirname = path.dirname(__filename);
  8. const inputFile = path.resolve(__dirname, '../library.excalidrawlib'); // 输入目录
  9. const outputFile = path.resolve(__dirname, '../lib/library.excalidrawlib'); // 输出文件路径
  10. interface ExportedLibraryData {
  11. type: string;
  12. version: number;
  13. source: string;
  14. libraryItems: LibraryItems;
  15. }
  16. function isExcalidrawlibFile(fileName: string): boolean {
  17. return fileName.endsWith('.excalidrawlib');
  18. }
  19. /**
  20. * checks if library item does not exist already in current library
  21. */
  22. const isUniqueItem = (
  23. existingLibraryItems: LibraryItems,
  24. targetLibraryItem: LibraryItem,
  25. ) => {
  26. return !existingLibraryItems.find((libraryItem) => {
  27. if (libraryItem.elements.length !== targetLibraryItem.elements.length) {
  28. return false;
  29. }
  30. // detect z-index difference by checking the excalidraw elements
  31. // are in order
  32. return libraryItem.elements.every((libItemExcalidrawItem, idx) => {
  33. return (
  34. libItemExcalidrawItem.id === targetLibraryItem.elements[idx].id &&
  35. libItemExcalidrawItem.versionNonce ===
  36. targetLibraryItem.elements[idx].versionNonce
  37. );
  38. });
  39. });
  40. };
  41. function positionError(obj: any, msg?: string): false {
  42. console.error({
  43. error_msg: msg,
  44. error_obj: obj
  45. })
  46. return false
  47. }
  48. function isLibraryItem(obj: any): obj is LibraryItem {
  49. if (!obj) return positionError(obj, "LibraryItem obj does not exist")
  50. if (!obj.id || !obj.status || !obj.elements || !obj.created) return positionError(obj, "obj missing fields")
  51. if (typeof obj.id !== 'string') return positionError(obj, "obj.id type is not string")
  52. if (obj.status !== "published" && obj.status !== "unpublished") return positionError(obj, "obj.status value is wrong")
  53. if (!Array.isArray(obj.elements)) return positionError(obj, "obj.elements is not an array")
  54. if (typeof obj.created !== 'number') return positionError(obj, "obj.created type is not number")
  55. return true
  56. }
  57. function isLibraryItems(obj: any): obj is LibraryItems {
  58. if (!obj) return positionError(obj, "LibraryItems obj does not exist")
  59. if (!Array.isArray(obj)) return positionError(obj, "obj is not an array")
  60. for (const item of obj) {
  61. if (!isLibraryItem(item)) return positionError(obj, "LibraryItems is not an array")
  62. }
  63. return true
  64. }
  65. function isExportedLibraryData(obj: any): obj is ExportedLibraryData {
  66. if (!obj) return positionError(obj, "ExportedLibraryData obj does not exist")
  67. if (obj.type !== "excalidrawlib") return positionError(obj, "obj.type value is not excalidrawlib")
  68. if (typeof obj.version !== "number") return positionError(obj, "obj.version type is not number")
  69. if (!obj.libraryItems || !isLibraryItems(obj.libraryItems)) return positionError(obj, "The library array does not exist or is wrong")
  70. return true
  71. }
  72. async function readExcalidrawlibFile(filePath: string): Promise<ExportedLibraryData> {
  73. const content = await readFile(filePath, 'utf-8');
  74. const parsed = JSON.parse(content);
  75. if (!isExportedLibraryData(parsed)) {
  76. console.error({filePath, parsed})
  77. throw new Error(`Invalid excalidrawlib file: ${filePath}`);
  78. }
  79. return parsed;
  80. }
  81. async function updateExcalidrawlibFile(inputFilePath: string, outputFilePath: string): Promise<ExportedLibraryData | undefined> {
  82. if (!isExcalidrawlibFile(inputFilePath)) return undefined
  83. if (!isExcalidrawlibFile(outputFilePath)) return undefined
  84. const originalData = await readExcalidrawlibFile(outputFilePath);
  85. const inputData = await readExcalidrawlibFile(inputFilePath);
  86. const exportedLibraryData: ExportedLibraryData = {
  87. type: originalData.type,
  88. version: originalData.version,
  89. source: originalData.source,
  90. libraryItems: []
  91. }
  92. inputData.libraryItems.map(item => {
  93. if (item.status === 'unpublished' && isUniqueItem(originalData.libraryItems, item)) {
  94. exportedLibraryData.libraryItems = [
  95. ...exportedLibraryData.libraryItems,
  96. {...item}
  97. ]
  98. }
  99. })
  100. return exportedLibraryData;
  101. }
  102. async function saveUpdatedExcalidrawlib(data: ExportedLibraryData, filePath: string) {
  103. const jsonString = JSON.stringify(data, null, 2); // 缩进美化
  104. await writeFile(filePath, jsonString, 'utf-8');
  105. console.log(`✅ 更新完成,已保存到 ${filePath}`);
  106. }
  107. // 执行流程
  108. const updated = await updateExcalidrawlibFile(inputFile, outputFile);
  109. if (updated !== undefined) {
  110. saveUpdatedExcalidrawlib(updated, outputFile);
  111. }