twl-generator 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "twl-generator",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Generate term-to-article lists from unfoldingWord en_tw archive for Bible books. Works in both Node.js (CLI) and React.js (browser) environments.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -116,24 +116,24 @@ class PrefixTrie {
116
116
 
117
117
  findMatches(text, startPos) {
118
118
  // First try exact case matches
119
- let matches = this._findMatchesInTree(this.exactCaseRoot, text, startPos, true);
119
+ let matches = this._findMatchesInTree(this.exactCaseRoot, text, startPos, true, text);
120
120
 
121
121
  // If no exact case matches, try case-insensitive
122
122
  if (matches.length === 0) {
123
- matches = this._findMatchesInTree(this.lowerCaseRoot, text.toLowerCase(), startPos, false);
123
+ matches = this._findMatchesInTree(this.lowerCaseRoot, text.toLowerCase(), startPos, false, text);
124
124
  }
125
125
 
126
126
  return matches;
127
127
  }
128
128
 
129
- _findMatchesInTree(root, text, startPos, isExactCase) {
129
+ _findMatchesInTree(root, searchText, startPos, isExactCase, originalText) {
130
130
  const matches = [];
131
131
  let node = root;
132
132
  let currentPos = startPos;
133
133
 
134
134
  // Try to match as long as possible
135
- while (currentPos < text.length) {
136
- const char = text[currentPos];
135
+ while (currentPos < searchText.length) {
136
+ const char = searchText[currentPos];
137
137
 
138
138
  if (!node[char]) {
139
139
  break; // No more matches possible
@@ -145,16 +145,17 @@ class PrefixTrie {
145
145
  // If we found terms at this position, collect them
146
146
  if (node._terms) {
147
147
  const matchLength = currentPos - startPos;
148
- const originalMatchedText = text.substring(startPos, currentPos);
148
+ // Always extract from the original text to preserve case
149
+ const originalMatchedText = originalText.substring(startPos, currentPos);
149
150
 
150
151
  // Check if this is a valid word boundary match (both start and end)
151
152
  const isStartBoundary = startPos === 0 ||
152
- /[\s\p{P}]/.test(text[startPos - 1]) ||
153
- !/[\w]/.test(text[startPos - 1]);
153
+ /[\s\p{P}]/.test(originalText[startPos - 1]) ||
154
+ !/[\w]/.test(originalText[startPos - 1]);
154
155
 
155
- const isEndBoundary = currentPos >= text.length ||
156
- /[\s\p{P}]/.test(text[currentPos]) ||
157
- !/[\w]/.test(text[currentPos]);
156
+ const isEndBoundary = currentPos >= originalText.length ||
157
+ /[\s\p{P}]/.test(originalText[currentPos]) ||
158
+ !/[\w]/.test(originalText[currentPos]);
158
159
 
159
160
  const isWordBoundary = isStartBoundary && isEndBoundary;
160
161
 
@@ -7,18 +7,7 @@ const isNode = typeof window === 'undefined' && typeof process !== 'undefined' &
7
7
 
8
8
  // Get appropriate fetch implementation
9
9
  async function getFetch() {
10
- if (isNode) {
11
- // Use native fetch in Node.js 18+ or fallback to node-fetch
12
- if (typeof globalThis.fetch !== 'undefined') {
13
- return globalThis.fetch;
14
- }
15
- try {
16
- const nodeFetch = await import('node-fetch');
17
- return nodeFetch.default;
18
- } catch (error) {
19
- throw new Error('fetch not available. Please use Node.js 18+ or install node-fetch');
20
- }
21
- }
10
+ // Both Node.js 18+ and browsers have native fetch
22
11
  return globalThis.fetch;
23
12
  }
24
13
 
@@ -7,8 +7,7 @@
7
7
  * import { generateTWTerms } from './utils/zipProcessor.js';
8
8
  * const terms = await generateTWTerms();
9
9
  */
10
-
11
- import { BibleBookData } from '../common/books.js';
10
+ import JSZip from "jszip";
12
11
 
13
12
  // Environment detection
14
13
  const isNode = typeof process !== 'undefined' && process.versions?.node;
@@ -21,38 +20,6 @@ const CACHE_VERSION = '1.0';
21
20
  // In-memory cache for processed terms (per session)
22
21
  let processedTermsCache = null;
23
22
 
24
- /**
25
- * Get dependencies dynamically (JSZip works in both environments)
26
- */
27
- async function getDeps() {
28
- try {
29
- const jsZipModule = await import('jszip');
30
- const deps = {
31
- JSZip: jsZipModule.default
32
- };
33
-
34
- // Add Node.js-specific fetch if needed (fallback for Node.js < 18)
35
- if (isNode) {
36
- if (typeof globalThis.fetch === 'undefined') {
37
- try {
38
- const nodeModule = await import('node-fetch');
39
- deps.fetch = nodeModule.default;
40
- } catch (error) {
41
- console.warn('node-fetch not available, please use Node.js 18+ or install node-fetch');
42
- deps.fetch = null;
43
- }
44
- } else {
45
- deps.fetch = globalThis.fetch;
46
- }
47
- }
48
-
49
- return deps;
50
- } catch (error) {
51
- console.error('Failed to load dependencies:', error);
52
- return null;
53
- }
54
- }
55
-
56
23
  async function getCachedZip() {
57
24
  if (isBrowser) {
58
25
  // Browser: Use localStorage for ZIP cache
@@ -144,26 +111,6 @@ async function getCachedTerms() {
144
111
  } catch (e) { /* ignore cleanup errors */ }
145
112
  }
146
113
  }
147
- } else if (isNode) {
148
- // Node.js file system caching
149
- try {
150
- const deps = await getNodeDeps();
151
- if (!deps) return null;
152
-
153
- const { fs, path, fileURLToPath } = deps;
154
- const __filename = fileURLToPath(import.meta.url);
155
- const __dirname = path.dirname(__filename);
156
- const CACHE_FILE = path.join(__dirname, '../../article_terms.json');
157
-
158
- if (fs.existsSync(CACHE_FILE)) {
159
- const cachedData = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
160
- console.log('Using cached article terms from article_terms.json');
161
- memoryCache = cachedData;
162
- return cachedData;
163
- }
164
- } catch (error) {
165
- console.log('File cache corrupted, regenerating...');
166
- }
167
114
  }
168
115
 
169
116
  return null;
@@ -192,22 +139,6 @@ async function cacheTerms(termMap) {
192
139
  console.warn('Failed to cache in browser storage:', error.message);
193
140
  }
194
141
  }
195
- } else if (isNode) {
196
- // Node.js file system caching
197
- try {
198
- const deps = await getNodeDeps();
199
- if (!deps) return;
200
-
201
- const { fs, path, fileURLToPath } = deps;
202
- const __filename = fileURLToPath(import.meta.url);
203
- const __dirname = path.dirname(__filename);
204
- const CACHE_FILE = path.join(__dirname, '../../article_terms.json');
205
-
206
- fs.writeFileSync(CACHE_FILE, JSON.stringify(termMap, null, 2), 'utf8');
207
- console.log('Article terms cached to article_terms.json');
208
- } catch (error) {
209
- console.warn('Failed to cache article terms to file:', error.message);
210
- }
211
142
  }
212
143
  }
213
144
 
@@ -215,11 +146,6 @@ async function cacheTerms(termMap) {
215
146
  * Process ZIP buffer and extract term mappings
216
147
  */
217
148
  async function processZipBuffer(zipBuffer) {
218
- // Use JSZip universally for both Node.js and Browser
219
- const deps = await getDeps();
220
- if (!deps) throw new Error('Failed to load dependencies');
221
- const { JSZip } = deps;
222
-
223
149
  const zip = new JSZip();
224
150
  const zipData = await zip.loadAsync(zipBuffer);
225
151
 
@@ -277,17 +203,7 @@ export async function generateTWTerms() {
277
203
  // Download fresh ZIP
278
204
  console.log('Downloading TW archive...');
279
205
 
280
- let fetchFn;
281
- if (isBrowser) {
282
- fetchFn = window.fetch;
283
- } else {
284
- const deps = await getDeps();
285
- fetchFn = deps?.fetch;
286
- }
287
-
288
- if (!fetchFn) throw new Error('Fetch not available');
289
-
290
- const res = await fetchFn(ZIP_URL);
206
+ const res = await fetch(ZIP_URL);
291
207
  if (!res.ok) throw new Error(`Failed to download ZIP: ${res.status} ${res.statusText}`);
292
208
 
293
209
  zipBuffer = await res.arrayBuffer();