Skip to content

Commit f4b2131

Browse files
committed
migration a zipAdapter for all
1 parent 2966716 commit f4b2131

9 files changed

Lines changed: 261 additions & 106 deletions

File tree

src/processors/gridset/helpers.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import * as path from 'path';
1111
import { execSync } from 'child_process';
1212
import Database from 'better-sqlite3';
1313
import { dotNetTicksToDate } from '../../utils/dotnetTicks';
14-
import { getZipEntriesWithPassword, resolveGridsetPasswordFromEnv } from './password';
14+
import { getZipEntriesFromAdapter, resolveGridsetPasswordFromEnv } from './password';
15+
import { openZipFromInput } from '../../utils/zip';
1516

1617
function normalizeZipPath(p: string): string {
1718
const unified = p.replace(/\\/g, '/');
@@ -64,10 +65,8 @@ export async function openImage(
6465
password = resolveGridsetPasswordFromEnv()
6566
): Promise<Uint8Array | null> {
6667
try {
67-
// eslint-disable-next-line @typescript-eslint/no-var-requires
68-
const JSZip = require('jszip') as typeof import('jszip');
69-
const zip = await JSZip.loadAsync(gridsetBuffer);
70-
const entries = getZipEntriesWithPassword(zip, password);
68+
const { zip } = await openZipFromInput(gridsetBuffer);
69+
const entries = getZipEntriesFromAdapter(zip, password);
7170
const want = normalizeZipPath(entryPath);
7271
const entry = entries.find((e) => normalizeZipPath(e.entryName) === want);
7372
if (!entry) return null;

src/processors/gridset/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export { ensureAlphaChannel, darkenColor, lightenColor, hexToRgba, rgbaToHex } f
6464
export {
6565
resolveGridsetPassword,
6666
getZipEntriesWithPassword,
67+
getZipEntriesFromAdapter,
6768
resolveGridsetPasswordFromEnv,
6869
} from './password';
6970

src/processors/gridset/password.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type JSZip from 'jszip';
22
import { ProcessorOptions } from '../../core/baseProcessor';
33
import { ProcessorInput } from '../../utils/io';
4+
import { type ZipAdapter } from '../../utils/zip';
45

56
function getExtension(source: string): string {
67
const index = source.lastIndexOf('.');
@@ -43,7 +44,7 @@ export function resolveGridsetPasswordFromEnv(): string | undefined {
4344
* @param password - Optional password (kept for API compatibility, not used with JSZip)
4445
* @returns Array of entry objects with name and data
4546
*/
46-
type ZipEntry = {
47+
export type ZipEntry = {
4748
name: string;
4849
entryName: string;
4950
dir: boolean;
@@ -81,3 +82,16 @@ export function getZipEntriesWithPassword(zip: JSZip, password?: string): ZipEnt
8182

8283
return entries;
8384
}
85+
86+
export function getZipEntriesFromAdapter(zip: ZipAdapter, password?: string): ZipEntry[] {
87+
if (password) {
88+
console.warn('Zip password support is handled at the archive level for .gridsetx files.');
89+
}
90+
91+
return zip.listFiles().map((entryName) => ({
92+
name: entryName,
93+
entryName,
94+
dir: false,
95+
getData: () => zip.readFile(entryName),
96+
}));
97+
}

src/processors/gridset/wordlistHelpers.ts

Lines changed: 89 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
*/
1111

1212
import { XMLParser, XMLBuilder } from 'fast-xml-parser';
13-
import type JSZip from 'jszip';
14-
import { getZipEntriesWithPassword, resolveGridsetPasswordFromEnv } from './password';
13+
import { getZipEntriesFromAdapter, resolveGridsetPasswordFromEnv } from './password';
14+
import { openZipFromInput } from '../../utils/zip';
15+
import { getNodeRequire, isNodeRuntime } from '../../utils/io';
1516
import { decodeText } from '../../utils/io';
1617

1718
/**
@@ -134,60 +135,57 @@ export async function extractWordlists(
134135
const wordlists = new Map<string, WordList>();
135136
const parser = new XMLParser();
136137

137-
let zip: JSZip;
138138
try {
139-
// eslint-disable-next-line @typescript-eslint/no-var-requires
140-
const JSZip = require('jszip') as typeof import('jszip');
141-
zip = await JSZip.loadAsync(gridsetBuffer);
142-
} catch (error: any) {
143-
throw new Error(`Invalid gridset buffer: ${error.message}`);
144-
}
145-
const entries = getZipEntriesWithPassword(zip, password);
139+
const { zip } = await openZipFromInput(gridsetBuffer);
140+
const entries = getZipEntriesFromAdapter(zip, password);
146141

147-
// Process each grid file
148-
for (const entry of entries) {
149-
if (entry.entryName.startsWith('Grids/') && entry.entryName.endsWith('grid.xml')) {
150-
try {
151-
const xmlContent = decodeText(await entry.getData());
152-
const data = parser.parse(xmlContent);
153-
const grid = data.Grid || data.grid;
142+
// Process each grid file
143+
for (const entry of entries) {
144+
if (entry.entryName.startsWith('Grids/') && entry.entryName.endsWith('grid.xml')) {
145+
try {
146+
const xmlContent = decodeText(await entry.getData());
147+
const data = parser.parse(xmlContent);
148+
const grid = data.Grid || data.grid;
154149

155-
if (!grid || !grid.WordList) {
156-
continue;
157-
}
150+
if (!grid || !grid.WordList) {
151+
continue;
152+
}
158153

159-
// Extract grid name from path (e.g., "Grids/MyGrid/grid.xml" -> "MyGrid")
160-
const match = entry.entryName.match(/^Grids\/([^/]+)\//);
161-
const gridName = match ? match[1] : entry.entryName;
154+
// Extract grid name from path (e.g., "Grids/MyGrid/grid.xml" -> "MyGrid")
155+
const match = entry.entryName.match(/^Grids\/([^/]+)\//);
156+
const gridName = match ? match[1] : entry.entryName;
162157

163-
// Parse wordlist items
164-
const wordlistData = grid.WordList;
165-
const itemsContainer = wordlistData.Items || wordlistData.items;
158+
// Parse wordlist items
159+
const wordlistData = grid.WordList;
160+
const itemsContainer = wordlistData.Items || wordlistData.items;
166161

167-
if (!itemsContainer) {
168-
continue;
169-
}
162+
if (!itemsContainer) {
163+
continue;
164+
}
170165

171-
const itemArray = Array.isArray(itemsContainer.WordListItem)
172-
? itemsContainer.WordListItem
173-
: itemsContainer.WordListItem
174-
? [itemsContainer.WordListItem]
175-
: [];
166+
const itemArray = Array.isArray(itemsContainer.WordListItem)
167+
? itemsContainer.WordListItem
168+
: itemsContainer.WordListItem
169+
? [itemsContainer.WordListItem]
170+
: [];
176171

177-
const items: WordListItem[] = itemArray.map((item: any) => ({
178-
text: item.Text?.s?.r || item.text?.s?.r || '',
179-
image: item.Image || item.image || undefined,
180-
partOfSpeech: item.PartOfSpeech || item.partOfSpeech || 'Unknown',
181-
}));
172+
const items: WordListItem[] = itemArray.map((item: any) => ({
173+
text: item.Text?.s?.r || item.text?.s?.r || '',
174+
image: item.Image || item.image || undefined,
175+
partOfSpeech: item.PartOfSpeech || item.partOfSpeech || 'Unknown',
176+
}));
182177

183-
if (items.length > 0) {
184-
wordlists.set(gridName, { items });
178+
if (items.length > 0) {
179+
wordlists.set(gridName, { items });
180+
}
181+
} catch (error) {
182+
// Skip grids with parsing errors
183+
console.warn(`Failed to extract wordlist from ${entry.entryName}:`, error);
185184
}
186-
} catch (error) {
187-
// Skip grids with parsing errors
188-
console.warn(`Failed to extract wordlist from ${entry.entryName}:`, error);
189185
}
190186
}
187+
} catch (error: any) {
188+
throw new Error(`Invalid gridset buffer: ${error.message}`);
191189
}
192190

193191
return wordlists;
@@ -221,15 +219,50 @@ export async function updateWordlist(
221219
suppressEmptyNode: false,
222220
});
223221

224-
let zip: JSZip;
222+
let entries: Array<{
223+
entryName: string;
224+
getData: () => Promise<Uint8Array>;
225+
}>;
226+
let saveZip: (() => Promise<Uint8Array>) | null = null;
227+
let updateEntry: ((entryName: string, xml: string) => void) | null = null;
228+
225229
try {
226-
// eslint-disable-next-line @typescript-eslint/no-var-requires
227-
const JSZip = require('jszip') as typeof import('jszip');
228-
zip = await JSZip.loadAsync(gridsetBuffer);
230+
if (isNodeRuntime()) {
231+
const AdmZip = getNodeRequire()('adm-zip') as typeof import('adm-zip');
232+
const zip = new AdmZip(Buffer.from(gridsetBuffer));
233+
entries = zip.getEntries().map((entry) => ({
234+
entryName: entry.entryName,
235+
getData: () => Promise.resolve(entry.getData()),
236+
}));
237+
updateEntry = (entryName: string, xml: string) => {
238+
zip.addFile(entryName, Buffer.from(xml, 'utf8'));
239+
};
240+
saveZip = () => Promise.resolve(zip.toBuffer());
241+
} else {
242+
const module = await import('jszip');
243+
const JSZip = module.default || module;
244+
const zip = await JSZip.loadAsync(gridsetBuffer);
245+
entries = getZipEntriesFromAdapter(
246+
{
247+
listFiles: () => Object.keys(zip.files),
248+
readFile: async (name: string) => {
249+
const file = zip.file(name);
250+
if (!file) {
251+
throw new Error(`Zip entry not found: ${name}`);
252+
}
253+
return file.async('uint8array');
254+
},
255+
},
256+
password
257+
);
258+
updateEntry = (entryName: string, xml: string) => {
259+
zip.file(entryName, xml, { binary: false });
260+
};
261+
saveZip = async () => zip.generateAsync({ type: 'uint8array' });
262+
}
229263
} catch (error: any) {
230264
throw new Error(`Invalid gridset buffer: ${error.message}`);
231265
}
232-
const entries = getZipEntriesWithPassword(zip, password);
233266

234267
let found = false;
235268

@@ -272,7 +305,9 @@ export async function updateWordlist(
272305

273306
// Rebuild the XML
274307
const updatedXml = builder.build(data);
275-
zip.file(entry.entryName, updatedXml, { binary: false });
308+
if (updateEntry) {
309+
updateEntry(entry.entryName, updatedXml);
310+
}
276311
found = true;
277312
} catch (error) {
278313
const message = error instanceof Error ? error.message : String(error);
@@ -286,5 +321,8 @@ export async function updateWordlist(
286321
throw new Error(`Grid "${gridName}" not found in gridset`);
287322
}
288323

289-
return await zip.generateAsync({ type: 'uint8array' });
324+
if (!saveZip) {
325+
throw new Error('Failed to serialize updated gridset.');
326+
}
327+
return await saveZip();
290328
}

0 commit comments

Comments
 (0)