@@ -22,6 +22,7 @@ import { ValidationResult } from '../validation/validationTypes';
2222import {
2323 ProcessorInput ,
2424 getFs ,
25+ getNodeRequire ,
2526 getOs ,
2627 getPath ,
2728 isNodeRuntime ,
@@ -38,30 +39,7 @@ import {
3839 requireBetterSqlite3 ,
3940 type SqliteDatabaseAdapter ,
4041} from '../utils/sqlite' ;
41- import type JSZip from 'jszip' ;
42-
43- type JSZipStatic = typeof JSZip ;
44- let JSZipModuleTouchChat : JSZipStatic | undefined ;
45- async function getJSZipTouchChat ( ) : Promise < JSZipStatic > {
46- if ( ! JSZipModuleTouchChat ) {
47- try {
48- const module = await import ( 'jszip' ) ;
49- JSZipModuleTouchChat = module . default || module ;
50- } catch ( error ) {
51- try {
52- // eslint-disable-next-line @typescript-eslint/no-var-requires
53- const module = require ( 'jszip' ) ;
54- JSZipModuleTouchChat = module . default || module ;
55- } catch {
56- throw new Error ( 'Zip handling requires JSZip in this environment.' ) ;
57- }
58- }
59- }
60- if ( ! JSZipModuleTouchChat ) {
61- throw new Error ( 'Zip handling requires JSZip in this environment.' ) ;
62- }
63- return JSZipModuleTouchChat ;
64- }
42+ import { openZipFromInput } from '../utils/zip' ;
6543
6644interface TouchChatButton {
6745 id : number ;
@@ -175,17 +153,12 @@ class TouchChatProcessor extends BaseProcessor {
175153
176154 // Step 1: Unzip
177155 const zipInput = readBinaryFromInput ( filePathOrBuffer ) ;
178- const JSZip = await getJSZipTouchChat ( ) ;
179- const zip = await JSZip . loadAsync ( zipInput ) ;
180- const vocabEntry = Object . keys ( zip . files ) . find ( ( name ) => name . endsWith ( '.c4v' ) ) ;
156+ const { zip } = await openZipFromInput ( zipInput ) ;
157+ const vocabEntry = zip . listFiles ( ) . find ( ( name ) => name . endsWith ( '.c4v' ) ) ;
181158 if ( ! vocabEntry ) {
182159 throw new Error ( 'No .c4v vocab DB found in TouchChat export' ) ;
183160 }
184- const vocabFile = zip . file ( vocabEntry ) ;
185- if ( ! vocabFile ) {
186- throw new Error ( 'Failed to read .c4v vocab DB from TouchChat export' ) ;
187- }
188- const dbBuffer = await vocabFile . async ( 'uint8array' ) ;
161+ const dbBuffer = await zip . readFile ( vocabEntry ) ;
189162 const dbResult = await openSqliteDatabase ( dbBuffer , { readonly : true } ) ;
190163 db = dbResult . db ;
191164 cleanup = dbResult . cleanup ;
@@ -1123,11 +1096,10 @@ class TouchChatProcessor extends BaseProcessor {
11231096 db . close ( ) ;
11241097
11251098 // Create zip file with the database
1126- const JSZip = await getJSZipTouchChat ( ) ;
1127- const zip = new JSZip ( ) ;
1128- zip . file ( 'vocab.c4v' , fs . readFileSync ( dbPath ) ) ;
1129- const zipBuffer = await zip . generateAsync ( { type : 'nodebuffer' } ) ;
1130- fs . writeFileSync ( outputPath , zipBuffer ) ;
1099+ const AdmZip = getNodeRequire ( ) ( 'adm-zip' ) as typeof import ( 'adm-zip' ) ;
1100+ const zip = new AdmZip ( ) ;
1101+ zip . addLocalFile ( dbPath , '' , 'vocab.c4v' ) ;
1102+ zip . writeZip ( outputPath ) ;
11311103 } finally {
11321104 // Clean up
11331105 if ( fs . existsSync ( tmpDir ) ) {
0 commit comments