Skip to content

Commit 2966716

Browse files
committed
migration to zipadaptor for obz/obf
1 parent 90b2d16 commit 2966716

1 file changed

Lines changed: 49 additions & 64 deletions

File tree

src/processors/obfProcessor.ts

Lines changed: 49 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -28,34 +28,11 @@ import {
2828
readTextFromInput,
2929
writeTextToPath,
3030
encodeBase64,
31+
decodeText,
32+
getNodeRequire,
33+
isNodeRuntime,
3134
} from '../utils/io';
32-
import type JSZip from 'jszip';
33-
34-
// Use dynamic import for JSZip to support both browser and Node environments
35-
type JSZipStatic = typeof JSZip;
36-
let JSZipModuleObf: JSZipStatic | undefined;
37-
async function getJSZipObf(): Promise<JSZipStatic> {
38-
if (!JSZipModuleObf) {
39-
try {
40-
// Try ES module import first (browser/Vite)
41-
const module = await import('jszip');
42-
JSZipModuleObf = module.default || module;
43-
} catch (error) {
44-
// Fall back to CommonJS require (Node.js)
45-
try {
46-
// eslint-disable-next-line @typescript-eslint/no-var-requires
47-
const module = require('jszip');
48-
JSZipModuleObf = module.default || module;
49-
} catch (err2) {
50-
throw new Error('Zip handling requires JSZip in this environment.');
51-
}
52-
}
53-
}
54-
if (!JSZipModuleObf) {
55-
throw new Error('Zip handling requires JSZip in this environment.');
56-
}
57-
return JSZipModuleObf;
58-
}
35+
import { openZipFromInput, type ZipAdapter } from '../utils/zip';
5936

6037
const OBF_FORMAT_VERSION = 'open-board-0.1';
6138

@@ -106,7 +83,7 @@ interface ObfBoard {
10683
}
10784

10885
class ObfProcessor extends BaseProcessor {
109-
private zipFile?: JSZip; // JSZip instance
86+
private zipFile?: ZipAdapter;
11087
private imageCache: Map<string, string> = new Map(); // Cache for data URLs
11188

11289
constructor(options?: ProcessorOptions) {
@@ -136,10 +113,12 @@ class ObfProcessor extends BaseProcessor {
136113

137114
for (const imagePath of possiblePaths) {
138115
try {
139-
const file = this.zipFile.file(imagePath as string);
140-
if (file) {
141-
const buffer = await file.async('nodebuffer');
142-
return buffer;
116+
const buffer = await this.zipFile.readFile(imagePath as string);
117+
if (buffer) {
118+
if (typeof Buffer !== 'undefined') {
119+
return Buffer.from(buffer);
120+
}
121+
return null;
143122
}
144123
} catch (err) {
145124
continue;
@@ -178,9 +157,8 @@ class ObfProcessor extends BaseProcessor {
178157

179158
for (const imagePath of possiblePaths) {
180159
try {
181-
const file = this.zipFile.file(imagePath as string);
182-
if (file) {
183-
const buffer = await file.async('uint8array');
160+
const buffer = await this.zipFile.readFile(imagePath as string);
161+
if (buffer) {
184162
const contentType =
185163
(imageData as { content_type?: string }).content_type ||
186164
this.getMimeTypeFromFilename(imagePath as string);
@@ -518,38 +496,29 @@ class ObfProcessor extends BaseProcessor {
518496
throw new Error('Invalid OBF content: not JSON and not ZIP');
519497
}
520498

521-
const JSZip = await getJSZipObf();
522-
let zip: JSZip;
523499
try {
524-
const zipInput = readBinaryFromInput(filePathOrBuffer);
525-
zip = await JSZip.loadAsync(zipInput);
500+
const zipResult = await openZipFromInput(filePathOrBuffer);
501+
this.zipFile = zipResult.zip;
526502
} catch (err) {
527-
console.error('[OBF] Error loading ZIP with JSZip:', err);
503+
console.error('[OBF] Error loading ZIP:', err);
528504
throw err;
529505
}
530506

531507
// Store the ZIP file reference for image extraction
532-
this.zipFile = zip;
533508
this.imageCache.clear(); // Clear cache for new file
534509

535510
console.log('[OBF] Detected zip archive, extracting .obf files');
536511

537512
// Collect all .obf entries
538-
const obfEntries: Array<{ name: string; file: JSZip.JSZipObject }> = [];
539-
zip.forEach((relativePath: string, file: JSZip.JSZipObject) => {
540-
if (file.dir) return;
541-
if (relativePath.toLowerCase().endsWith('.obf')) {
542-
obfEntries.push({ name: relativePath, file });
543-
}
544-
});
513+
const obfEntries = this.zipFile.listFiles().filter((name) => name.toLowerCase().endsWith('.obf'));
545514

546515
// Process each .obf entry
547-
for (const entry of obfEntries) {
516+
for (const entryName of obfEntries) {
548517
try {
549-
const content = await entry.file.async('string');
550-
const boardData = tryParseObfJson(content);
518+
const content = await this.zipFile.readFile(entryName);
519+
const boardData = tryParseObfJson(decodeText(content));
551520
if (boardData) {
552-
const page = await this.processBoard(boardData, entry.name);
521+
const page = await this.processBoard(boardData, entryName);
553522
tree.addPage(page);
554523

555524
// Set metadata if not already set (use first board as reference)
@@ -564,10 +533,10 @@ class ObfProcessor extends BaseProcessor {
564533
tree.rootId = page.id;
565534
}
566535
} else {
567-
console.warn('[OBF] Skipped entry (not valid OBF JSON):', entry.name);
536+
console.warn('[OBF] Skipped entry (not valid OBF JSON):', entryName);
568537
}
569538
} catch (err) {
570-
console.warn('[OBF] Error processing entry:', entry.name, err);
539+
console.warn('[OBF] Error processing entry:', entryName, err);
571540
}
572541
}
573542

@@ -722,18 +691,34 @@ class ObfProcessor extends BaseProcessor {
722691
writeTextToPath(outputPath, JSON.stringify(obfBoard, null, 2));
723692
} else {
724693
// Save as OBZ (zip with multiple OBF files)
725-
const JSZip = await getJSZipObf();
726-
const zip = new JSZip();
694+
if (isNodeRuntime()) {
695+
const AdmZip = getNodeRequire()('adm-zip') as typeof import('adm-zip');
696+
const zip = new AdmZip();
697+
698+
Object.values(tree.pages).forEach((page) => {
699+
const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
700+
const obfContent = JSON.stringify(obfBoard, null, 2);
701+
zip.addFile(`${page.id}.obf`, Buffer.from(obfContent, 'utf8'));
702+
});
727703

728-
Object.values(tree.pages).forEach((page) => {
729-
const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
730-
const obfContent = JSON.stringify(obfBoard, null, 2);
731-
zip.file(`${page.id}.obf`, obfContent);
732-
});
704+
const zipBuffer = zip.toBuffer();
705+
const { writeBinaryToPath } = await import('../utils/io');
706+
writeBinaryToPath(outputPath, zipBuffer);
707+
} else {
708+
const module = await import('jszip');
709+
const JSZip = module.default || module;
710+
const zip = new JSZip();
711+
712+
Object.values(tree.pages).forEach((page) => {
713+
const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
714+
const obfContent = JSON.stringify(obfBoard, null, 2);
715+
zip.file(`${page.id}.obf`, obfContent);
716+
});
733717

734-
const zipBuffer = await zip.generateAsync({ type: 'uint8array' });
735-
const { writeBinaryToPath } = await import('../utils/io');
736-
writeBinaryToPath(outputPath, zipBuffer);
718+
const zipBuffer = await zip.generateAsync({ type: 'uint8array' });
719+
const { writeBinaryToPath } = await import('../utils/io');
720+
writeBinaryToPath(outputPath, zipBuffer);
721+
}
737722
}
738723
}
739724

0 commit comments

Comments
 (0)