@fgv/ts-res-ui-components 5.0.0-14 → 5.0.0-16
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.
|
@@ -17,17 +17,21 @@ export declare class BrowserZipLoader implements IZipLoader {
|
|
|
17
17
|
*/
|
|
18
18
|
loadFromUrl(url: string, options?: ZipLoadOptions, onProgress?: ZipProgressCallback): Promise<Result<ZipLoadResult>>;
|
|
19
19
|
/**
|
|
20
|
-
* Build file tree from
|
|
20
|
+
* Build file tree from ZipFileTreeAccessors (using ts-extras)
|
|
21
21
|
*/
|
|
22
|
-
private
|
|
22
|
+
private buildFileTreeFromAccessors;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* Recursively process file tree items
|
|
25
25
|
*/
|
|
26
|
-
private
|
|
26
|
+
private processFileTreeItems;
|
|
27
27
|
/**
|
|
28
|
-
* Load
|
|
28
|
+
* Load manifest from ZIP using ZipFileTreeAccessors
|
|
29
29
|
*/
|
|
30
|
-
private
|
|
30
|
+
private loadManifestFromAccessors;
|
|
31
|
+
/**
|
|
32
|
+
* Load configuration from ZIP using ZipFileTreeAccessors
|
|
33
|
+
*/
|
|
34
|
+
private loadConfigurationFromAccessors;
|
|
31
35
|
/**
|
|
32
36
|
* Find common root directory from file paths
|
|
33
37
|
*/
|
|
@@ -1,29 +1,7 @@
|
|
|
1
1
|
import { succeed, fail } from '@fgv/ts-utils';
|
|
2
|
+
import { ZipFileTree as ExtrasZipFileTree } from '@fgv/ts-extras';
|
|
2
3
|
import { parseManifest, parseConfiguration, zipTreeToFiles, zipTreeToDirectory, isZipFile } from './zipUtils';
|
|
3
4
|
import { processImportedFiles, processImportedDirectory } from '../tsResIntegration';
|
|
4
|
-
// Dynamic import for JSZip to support both Node.js and browser environments
|
|
5
|
-
let JSZip = null;
|
|
6
|
-
/**
|
|
7
|
-
* Get JSZip instance (assumes JSZip is available)
|
|
8
|
-
*/
|
|
9
|
-
function getJSZip() {
|
|
10
|
-
if (JSZip)
|
|
11
|
-
return JSZip;
|
|
12
|
-
// Check if JSZip is globally available
|
|
13
|
-
if (typeof window !== 'undefined' && window.JSZip) {
|
|
14
|
-
JSZip = window.JSZip;
|
|
15
|
-
return JSZip;
|
|
16
|
-
}
|
|
17
|
-
// Try to get JSZip from require/import (will work in bundled environments)
|
|
18
|
-
try {
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
20
|
-
JSZip = require('jszip');
|
|
21
|
-
return JSZip;
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
throw new Error('JSZip is not available. Please install jszip as a dependency: npm install jszip');
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
5
|
/**
|
|
28
6
|
* Browser-based ZIP loader implementation
|
|
29
7
|
*/
|
|
@@ -47,27 +25,27 @@ export class BrowserZipLoader {
|
|
|
47
25
|
*/
|
|
48
26
|
async loadFromBuffer(buffer, options = {}, onProgress) {
|
|
49
27
|
onProgress?.('parsing-zip', 0, 'Parsing ZIP archive');
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
28
|
+
const zipAccessorsResult = ExtrasZipFileTree.ZipFileTreeAccessors.fromBuffer(buffer);
|
|
29
|
+
if (zipAccessorsResult.isFailure()) {
|
|
30
|
+
return fail(`Failed to parse ZIP: ${zipAccessorsResult.message}`);
|
|
31
|
+
}
|
|
32
|
+
const zipAccessors = zipAccessorsResult.value;
|
|
55
33
|
onProgress?.('parsing-zip', 100, 'ZIP archive parsed');
|
|
56
|
-
// Build file tree
|
|
57
|
-
const fileTree = await this.
|
|
34
|
+
// Build file tree using the new adapter
|
|
35
|
+
const fileTree = await this.buildFileTreeFromAccessors(zipAccessors, onProgress);
|
|
58
36
|
// Load manifest
|
|
59
37
|
onProgress?.('loading-manifest', 0, 'Loading manifest');
|
|
60
|
-
const manifest = await this.
|
|
38
|
+
const manifest = await this.loadManifestFromAccessors(zipAccessors);
|
|
61
39
|
onProgress?.('loading-manifest', 100, 'Manifest loaded');
|
|
62
40
|
// Load configuration
|
|
63
41
|
onProgress?.('loading-config', 0, 'Loading configuration');
|
|
64
|
-
const config = await this.
|
|
42
|
+
const config = await this.loadConfigurationFromAccessors(zipAccessors, options);
|
|
65
43
|
onProgress?.('loading-config', 100, 'Configuration loaded');
|
|
66
44
|
// Extract files and directory structure
|
|
67
45
|
onProgress?.('extracting-files', 0, 'Extracting files');
|
|
68
|
-
const
|
|
46
|
+
const filesList = zipTreeToFiles(fileTree);
|
|
69
47
|
const directory = zipTreeToDirectory(fileTree);
|
|
70
|
-
onProgress?.('extracting-files', 100, `Extracted ${
|
|
48
|
+
onProgress?.('extracting-files', 100, `Extracted ${filesList.length} files`);
|
|
71
49
|
// Process resources if requested
|
|
72
50
|
let processedResources = null;
|
|
73
51
|
if (options.autoProcessResources) {
|
|
@@ -82,8 +60,8 @@ export class BrowserZipLoader {
|
|
|
82
60
|
throw new Error(`Failed to process resources from directory: ${processResult.message}`);
|
|
83
61
|
}
|
|
84
62
|
}
|
|
85
|
-
else if (
|
|
86
|
-
const processResult = await processImportedFiles(
|
|
63
|
+
else if (filesList.length > 0) {
|
|
64
|
+
const processResult = await processImportedFiles(filesList, configToUse || undefined);
|
|
87
65
|
if (processResult.isSuccess()) {
|
|
88
66
|
processedResources = processResult.value;
|
|
89
67
|
}
|
|
@@ -97,7 +75,7 @@ export class BrowserZipLoader {
|
|
|
97
75
|
return succeed({
|
|
98
76
|
manifest,
|
|
99
77
|
config: options.overrideConfig || config,
|
|
100
|
-
files,
|
|
78
|
+
files: filesList,
|
|
101
79
|
directory,
|
|
102
80
|
processedResources
|
|
103
81
|
});
|
|
@@ -120,86 +98,85 @@ export class BrowserZipLoader {
|
|
|
120
98
|
return this.loadFromBuffer(buffer, options, onProgress);
|
|
121
99
|
}
|
|
122
100
|
/**
|
|
123
|
-
* Build file tree from
|
|
101
|
+
* Build file tree from ZipFileTreeAccessors (using ts-extras)
|
|
124
102
|
*/
|
|
125
|
-
async
|
|
103
|
+
async buildFileTreeFromAccessors(zipAccessors, onProgress) {
|
|
126
104
|
const files = new Map();
|
|
127
105
|
const directories = new Set();
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
106
|
+
// Get all children from root
|
|
107
|
+
const rootChildrenResult = zipAccessors.getChildren('/');
|
|
108
|
+
if (rootChildrenResult.isFailure()) {
|
|
109
|
+
throw new Error(`Failed to read ZIP contents: ${rootChildrenResult.message}`);
|
|
110
|
+
}
|
|
111
|
+
// Process all items recursively
|
|
112
|
+
await this.processFileTreeItems(zipAccessors, '/', rootChildrenResult.value, files, directories, onProgress);
|
|
113
|
+
return {
|
|
114
|
+
files,
|
|
115
|
+
directories,
|
|
116
|
+
root: this.findCommonRoot(Array.from(files.keys()))
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Recursively process file tree items
|
|
121
|
+
*/
|
|
122
|
+
async processFileTreeItems(zipAccessors, currentPath, items, files, directories, onProgress, processed = { count: 0 }) {
|
|
123
|
+
for (const item of items) {
|
|
124
|
+
const itemPath = item.absolutePath.startsWith('/') ? item.absolutePath.substring(1) : item.absolutePath;
|
|
125
|
+
if (item.type === 'directory') {
|
|
126
|
+
directories.add(itemPath);
|
|
127
|
+
files.set(itemPath, {
|
|
128
|
+
name: item.name,
|
|
129
|
+
path: itemPath,
|
|
141
130
|
size: 0,
|
|
142
131
|
isDirectory: true,
|
|
143
|
-
lastModified:
|
|
132
|
+
lastModified: undefined
|
|
144
133
|
});
|
|
134
|
+
// Recursively process children
|
|
135
|
+
const childrenResult = zipAccessors.getChildren(item.absolutePath);
|
|
136
|
+
if (childrenResult.isSuccess()) {
|
|
137
|
+
await this.processFileTreeItems(zipAccessors, item.absolutePath, childrenResult.value, files, directories, onProgress, processed);
|
|
138
|
+
}
|
|
145
139
|
}
|
|
146
|
-
else {
|
|
147
|
-
//
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
140
|
+
else if (item.type === 'file') {
|
|
141
|
+
// Get file content
|
|
142
|
+
const contentResult = item.getRawContents();
|
|
143
|
+
const content = contentResult.isSuccess() ? contentResult.value : '';
|
|
144
|
+
files.set(itemPath, {
|
|
145
|
+
name: item.name,
|
|
146
|
+
path: itemPath,
|
|
152
147
|
size: content.length,
|
|
153
148
|
isDirectory: false,
|
|
154
|
-
lastModified:
|
|
149
|
+
lastModified: undefined,
|
|
155
150
|
content
|
|
156
151
|
});
|
|
157
152
|
}
|
|
158
|
-
processed++;
|
|
159
|
-
|
|
160
|
-
onProgress?.('extracting-files', progress, `Processing ${filename}`);
|
|
153
|
+
processed.count++;
|
|
154
|
+
onProgress?.('extracting-files', processed.count, `Processing ${itemPath}`);
|
|
161
155
|
}
|
|
162
|
-
return {
|
|
163
|
-
files,
|
|
164
|
-
directories,
|
|
165
|
-
root: this.findCommonRoot(Array.from(files.keys()))
|
|
166
|
-
};
|
|
167
156
|
}
|
|
168
157
|
/**
|
|
169
|
-
* Load manifest from ZIP
|
|
158
|
+
* Load manifest from ZIP using ZipFileTreeAccessors
|
|
170
159
|
*/
|
|
171
|
-
async
|
|
172
|
-
const
|
|
173
|
-
if (
|
|
160
|
+
async loadManifestFromAccessors(zipAccessors) {
|
|
161
|
+
const manifestResult = zipAccessors.getFileContents('manifest.json');
|
|
162
|
+
if (manifestResult.isFailure()) {
|
|
174
163
|
return null;
|
|
175
164
|
}
|
|
176
|
-
const
|
|
177
|
-
console.warn('Failed to read manifest file:', error);
|
|
178
|
-
return null;
|
|
179
|
-
});
|
|
180
|
-
if (!manifestData)
|
|
181
|
-
return null;
|
|
182
|
-
const parseResult = parseManifest(manifestData);
|
|
165
|
+
const parseResult = parseManifest(manifestResult.value);
|
|
183
166
|
return parseResult.orDefault() ?? null;
|
|
184
167
|
}
|
|
185
168
|
/**
|
|
186
|
-
* Load configuration from ZIP
|
|
169
|
+
* Load configuration from ZIP using ZipFileTreeAccessors
|
|
187
170
|
*/
|
|
188
|
-
async
|
|
171
|
+
async loadConfigurationFromAccessors(zipAccessors, options) {
|
|
189
172
|
if (options.overrideConfig) {
|
|
190
173
|
return options.overrideConfig;
|
|
191
174
|
}
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
175
|
+
const configResult = zipAccessors.getFileContents('config.json');
|
|
176
|
+
if (configResult.isFailure()) {
|
|
194
177
|
return null;
|
|
195
178
|
}
|
|
196
|
-
const
|
|
197
|
-
console.warn('Failed to read config file:', error);
|
|
198
|
-
return null;
|
|
199
|
-
});
|
|
200
|
-
if (!configData)
|
|
201
|
-
return null;
|
|
202
|
-
const parseResult = parseConfiguration(configData);
|
|
179
|
+
const parseResult = parseConfiguration(configResult.value);
|
|
203
180
|
return parseResult.orDefault() ?? null;
|
|
204
181
|
}
|
|
205
182
|
/**
|
|
@@ -5,31 +5,9 @@ exports.createBrowserZipLoader = createBrowserZipLoader;
|
|
|
5
5
|
exports.loadZipFile = loadZipFile;
|
|
6
6
|
exports.loadZipFromUrl = loadZipFromUrl;
|
|
7
7
|
const ts_utils_1 = require("@fgv/ts-utils");
|
|
8
|
+
const ts_extras_1 = require("@fgv/ts-extras");
|
|
8
9
|
const zipUtils_1 = require("./zipUtils");
|
|
9
10
|
const tsResIntegration_1 = require("../tsResIntegration");
|
|
10
|
-
// Dynamic import for JSZip to support both Node.js and browser environments
|
|
11
|
-
let JSZip = null;
|
|
12
|
-
/**
|
|
13
|
-
* Get JSZip instance (assumes JSZip is available)
|
|
14
|
-
*/
|
|
15
|
-
function getJSZip() {
|
|
16
|
-
if (JSZip)
|
|
17
|
-
return JSZip;
|
|
18
|
-
// Check if JSZip is globally available
|
|
19
|
-
if (typeof window !== 'undefined' && window.JSZip) {
|
|
20
|
-
JSZip = window.JSZip;
|
|
21
|
-
return JSZip;
|
|
22
|
-
}
|
|
23
|
-
// Try to get JSZip from require/import (will work in bundled environments)
|
|
24
|
-
try {
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
26
|
-
JSZip = require('jszip');
|
|
27
|
-
return JSZip;
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
throw new Error('JSZip is not available. Please install jszip as a dependency: npm install jszip');
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
11
|
/**
|
|
34
12
|
* Browser-based ZIP loader implementation
|
|
35
13
|
*/
|
|
@@ -53,27 +31,27 @@ class BrowserZipLoader {
|
|
|
53
31
|
*/
|
|
54
32
|
async loadFromBuffer(buffer, options = {}, onProgress) {
|
|
55
33
|
onProgress?.('parsing-zip', 0, 'Parsing ZIP archive');
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
34
|
+
const zipAccessorsResult = ts_extras_1.ZipFileTree.ZipFileTreeAccessors.fromBuffer(buffer);
|
|
35
|
+
if (zipAccessorsResult.isFailure()) {
|
|
36
|
+
return (0, ts_utils_1.fail)(`Failed to parse ZIP: ${zipAccessorsResult.message}`);
|
|
37
|
+
}
|
|
38
|
+
const zipAccessors = zipAccessorsResult.value;
|
|
61
39
|
onProgress?.('parsing-zip', 100, 'ZIP archive parsed');
|
|
62
|
-
// Build file tree
|
|
63
|
-
const fileTree = await this.
|
|
40
|
+
// Build file tree using the new adapter
|
|
41
|
+
const fileTree = await this.buildFileTreeFromAccessors(zipAccessors, onProgress);
|
|
64
42
|
// Load manifest
|
|
65
43
|
onProgress?.('loading-manifest', 0, 'Loading manifest');
|
|
66
|
-
const manifest = await this.
|
|
44
|
+
const manifest = await this.loadManifestFromAccessors(zipAccessors);
|
|
67
45
|
onProgress?.('loading-manifest', 100, 'Manifest loaded');
|
|
68
46
|
// Load configuration
|
|
69
47
|
onProgress?.('loading-config', 0, 'Loading configuration');
|
|
70
|
-
const config = await this.
|
|
48
|
+
const config = await this.loadConfigurationFromAccessors(zipAccessors, options);
|
|
71
49
|
onProgress?.('loading-config', 100, 'Configuration loaded');
|
|
72
50
|
// Extract files and directory structure
|
|
73
51
|
onProgress?.('extracting-files', 0, 'Extracting files');
|
|
74
|
-
const
|
|
52
|
+
const filesList = (0, zipUtils_1.zipTreeToFiles)(fileTree);
|
|
75
53
|
const directory = (0, zipUtils_1.zipTreeToDirectory)(fileTree);
|
|
76
|
-
onProgress?.('extracting-files', 100, `Extracted ${
|
|
54
|
+
onProgress?.('extracting-files', 100, `Extracted ${filesList.length} files`);
|
|
77
55
|
// Process resources if requested
|
|
78
56
|
let processedResources = null;
|
|
79
57
|
if (options.autoProcessResources) {
|
|
@@ -88,8 +66,8 @@ class BrowserZipLoader {
|
|
|
88
66
|
throw new Error(`Failed to process resources from directory: ${processResult.message}`);
|
|
89
67
|
}
|
|
90
68
|
}
|
|
91
|
-
else if (
|
|
92
|
-
const processResult = await (0, tsResIntegration_1.processImportedFiles)(
|
|
69
|
+
else if (filesList.length > 0) {
|
|
70
|
+
const processResult = await (0, tsResIntegration_1.processImportedFiles)(filesList, configToUse || undefined);
|
|
93
71
|
if (processResult.isSuccess()) {
|
|
94
72
|
processedResources = processResult.value;
|
|
95
73
|
}
|
|
@@ -103,7 +81,7 @@ class BrowserZipLoader {
|
|
|
103
81
|
return (0, ts_utils_1.succeed)({
|
|
104
82
|
manifest,
|
|
105
83
|
config: options.overrideConfig || config,
|
|
106
|
-
files,
|
|
84
|
+
files: filesList,
|
|
107
85
|
directory,
|
|
108
86
|
processedResources
|
|
109
87
|
});
|
|
@@ -126,86 +104,85 @@ class BrowserZipLoader {
|
|
|
126
104
|
return this.loadFromBuffer(buffer, options, onProgress);
|
|
127
105
|
}
|
|
128
106
|
/**
|
|
129
|
-
* Build file tree from
|
|
107
|
+
* Build file tree from ZipFileTreeAccessors (using ts-extras)
|
|
130
108
|
*/
|
|
131
|
-
async
|
|
109
|
+
async buildFileTreeFromAccessors(zipAccessors, onProgress) {
|
|
132
110
|
const files = new Map();
|
|
133
111
|
const directories = new Set();
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
112
|
+
// Get all children from root
|
|
113
|
+
const rootChildrenResult = zipAccessors.getChildren('/');
|
|
114
|
+
if (rootChildrenResult.isFailure()) {
|
|
115
|
+
throw new Error(`Failed to read ZIP contents: ${rootChildrenResult.message}`);
|
|
116
|
+
}
|
|
117
|
+
// Process all items recursively
|
|
118
|
+
await this.processFileTreeItems(zipAccessors, '/', rootChildrenResult.value, files, directories, onProgress);
|
|
119
|
+
return {
|
|
120
|
+
files,
|
|
121
|
+
directories,
|
|
122
|
+
root: this.findCommonRoot(Array.from(files.keys()))
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Recursively process file tree items
|
|
127
|
+
*/
|
|
128
|
+
async processFileTreeItems(zipAccessors, currentPath, items, files, directories, onProgress, processed = { count: 0 }) {
|
|
129
|
+
for (const item of items) {
|
|
130
|
+
const itemPath = item.absolutePath.startsWith('/') ? item.absolutePath.substring(1) : item.absolutePath;
|
|
131
|
+
if (item.type === 'directory') {
|
|
132
|
+
directories.add(itemPath);
|
|
133
|
+
files.set(itemPath, {
|
|
134
|
+
name: item.name,
|
|
135
|
+
path: itemPath,
|
|
147
136
|
size: 0,
|
|
148
137
|
isDirectory: true,
|
|
149
|
-
lastModified:
|
|
138
|
+
lastModified: undefined
|
|
150
139
|
});
|
|
140
|
+
// Recursively process children
|
|
141
|
+
const childrenResult = zipAccessors.getChildren(item.absolutePath);
|
|
142
|
+
if (childrenResult.isSuccess()) {
|
|
143
|
+
await this.processFileTreeItems(zipAccessors, item.absolutePath, childrenResult.value, files, directories, onProgress, processed);
|
|
144
|
+
}
|
|
151
145
|
}
|
|
152
|
-
else {
|
|
153
|
-
//
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
146
|
+
else if (item.type === 'file') {
|
|
147
|
+
// Get file content
|
|
148
|
+
const contentResult = item.getRawContents();
|
|
149
|
+
const content = contentResult.isSuccess() ? contentResult.value : '';
|
|
150
|
+
files.set(itemPath, {
|
|
151
|
+
name: item.name,
|
|
152
|
+
path: itemPath,
|
|
158
153
|
size: content.length,
|
|
159
154
|
isDirectory: false,
|
|
160
|
-
lastModified:
|
|
155
|
+
lastModified: undefined,
|
|
161
156
|
content
|
|
162
157
|
});
|
|
163
158
|
}
|
|
164
|
-
processed++;
|
|
165
|
-
|
|
166
|
-
onProgress?.('extracting-files', progress, `Processing ${filename}`);
|
|
159
|
+
processed.count++;
|
|
160
|
+
onProgress?.('extracting-files', processed.count, `Processing ${itemPath}`);
|
|
167
161
|
}
|
|
168
|
-
return {
|
|
169
|
-
files,
|
|
170
|
-
directories,
|
|
171
|
-
root: this.findCommonRoot(Array.from(files.keys()))
|
|
172
|
-
};
|
|
173
162
|
}
|
|
174
163
|
/**
|
|
175
|
-
* Load manifest from ZIP
|
|
164
|
+
* Load manifest from ZIP using ZipFileTreeAccessors
|
|
176
165
|
*/
|
|
177
|
-
async
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
166
|
+
async loadManifestFromAccessors(zipAccessors) {
|
|
167
|
+
const manifestResult = zipAccessors.getFileContents('manifest.json');
|
|
168
|
+
if (manifestResult.isFailure()) {
|
|
180
169
|
return null;
|
|
181
170
|
}
|
|
182
|
-
const
|
|
183
|
-
console.warn('Failed to read manifest file:', error);
|
|
184
|
-
return null;
|
|
185
|
-
});
|
|
186
|
-
if (!manifestData)
|
|
187
|
-
return null;
|
|
188
|
-
const parseResult = (0, zipUtils_1.parseManifest)(manifestData);
|
|
171
|
+
const parseResult = (0, zipUtils_1.parseManifest)(manifestResult.value);
|
|
189
172
|
return parseResult.orDefault() ?? null;
|
|
190
173
|
}
|
|
191
174
|
/**
|
|
192
|
-
* Load configuration from ZIP
|
|
175
|
+
* Load configuration from ZIP using ZipFileTreeAccessors
|
|
193
176
|
*/
|
|
194
|
-
async
|
|
177
|
+
async loadConfigurationFromAccessors(zipAccessors, options) {
|
|
195
178
|
if (options.overrideConfig) {
|
|
196
179
|
return options.overrideConfig;
|
|
197
180
|
}
|
|
198
|
-
const
|
|
199
|
-
if (
|
|
181
|
+
const configResult = zipAccessors.getFileContents('config.json');
|
|
182
|
+
if (configResult.isFailure()) {
|
|
200
183
|
return null;
|
|
201
184
|
}
|
|
202
|
-
const
|
|
203
|
-
console.warn('Failed to read config file:', error);
|
|
204
|
-
return null;
|
|
205
|
-
});
|
|
206
|
-
if (!configData)
|
|
207
|
-
return null;
|
|
208
|
-
const parseResult = (0, zipUtils_1.parseConfiguration)(configData);
|
|
185
|
+
const parseResult = (0, zipUtils_1.parseConfiguration)(configResult.value);
|
|
209
186
|
return parseResult.orDefault() ?? null;
|
|
210
187
|
}
|
|
211
188
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fgv/ts-res-ui-components",
|
|
3
|
-
"version": "5.0.0-
|
|
3
|
+
"version": "5.0.0-16",
|
|
4
4
|
"description": "Reusable React components for ts-res resource visualization and management",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
"homepage": "https://github.com/ErikFortune/fgv/tree/main/libraries/ts-res-ui-components#readme",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@heroicons/react": "~2.2.0",
|
|
24
|
-
"jszip": "~3.10.1",
|
|
25
24
|
"tslib": "^2.8.1",
|
|
26
25
|
"json-edit-react": "~1.28.2",
|
|
27
|
-
"@fgv/ts-
|
|
28
|
-
"@fgv/ts-
|
|
29
|
-
"@fgv/ts-json": "5.0.0-
|
|
30
|
-
"@fgv/ts-
|
|
26
|
+
"@fgv/ts-utils": "5.0.0-16",
|
|
27
|
+
"@fgv/ts-res": "5.0.0-16",
|
|
28
|
+
"@fgv/ts-json": "5.0.0-16",
|
|
29
|
+
"@fgv/ts-extras": "5.0.0-16",
|
|
30
|
+
"@fgv/ts-json-base": "5.0.0-16"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"react": ">=18.0.0",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@rushstack/heft-web-rig": "0.29.4",
|
|
57
57
|
"@rushstack/eslint-config": "~4.4.0",
|
|
58
58
|
"@rushstack/heft-jest-plugin": "~0.16.11",
|
|
59
|
-
"@fgv/ts-utils-jest": "5.0.0-
|
|
59
|
+
"@fgv/ts-utils-jest": "5.0.0-16"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "heft build --clean",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Result, succeed, fail } from '@fgv/ts-utils';
|
|
2
|
+
import { ZipFileTree as ExtrasZipFileTree } from '@fgv/ts-extras';
|
|
2
3
|
import {
|
|
3
4
|
IZipLoader,
|
|
4
5
|
ZipLoadOptions,
|
|
@@ -12,31 +13,6 @@ import { parseManifest, parseConfiguration, zipTreeToFiles, zipTreeToDirectory,
|
|
|
12
13
|
import { processImportedFiles, processImportedDirectory } from '../tsResIntegration';
|
|
13
14
|
import { ProcessedResources } from '../../types';
|
|
14
15
|
|
|
15
|
-
// Dynamic import for JSZip to support both Node.js and browser environments
|
|
16
|
-
let JSZip: any = null;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Get JSZip instance (assumes JSZip is available)
|
|
20
|
-
*/
|
|
21
|
-
function getJSZip(): any {
|
|
22
|
-
if (JSZip) return JSZip;
|
|
23
|
-
|
|
24
|
-
// Check if JSZip is globally available
|
|
25
|
-
if (typeof window !== 'undefined' && (window as any).JSZip) {
|
|
26
|
-
JSZip = (window as any).JSZip;
|
|
27
|
-
return JSZip;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Try to get JSZip from require/import (will work in bundled environments)
|
|
31
|
-
try {
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
33
|
-
JSZip = require('jszip');
|
|
34
|
-
return JSZip;
|
|
35
|
-
} catch (error) {
|
|
36
|
-
throw new Error('JSZip is not available. Please install jszip as a dependency: npm install jszip');
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
16
|
/**
|
|
41
17
|
* Browser-based ZIP loader implementation
|
|
42
18
|
*/
|
|
@@ -73,32 +49,32 @@ export class BrowserZipLoader implements IZipLoader {
|
|
|
73
49
|
): Promise<Result<ZipLoadResult>> {
|
|
74
50
|
onProgress?.('parsing-zip', 0, 'Parsing ZIP archive');
|
|
75
51
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
});
|
|
52
|
+
const zipAccessorsResult = ExtrasZipFileTree.ZipFileTreeAccessors.fromBuffer(buffer);
|
|
53
|
+
if (zipAccessorsResult.isFailure()) {
|
|
54
|
+
return fail(`Failed to parse ZIP: ${zipAccessorsResult.message}`);
|
|
55
|
+
}
|
|
81
56
|
|
|
57
|
+
const zipAccessors = zipAccessorsResult.value;
|
|
82
58
|
onProgress?.('parsing-zip', 100, 'ZIP archive parsed');
|
|
83
59
|
|
|
84
|
-
// Build file tree
|
|
85
|
-
const fileTree = await this.
|
|
60
|
+
// Build file tree using the new adapter
|
|
61
|
+
const fileTree = await this.buildFileTreeFromAccessors(zipAccessors, onProgress);
|
|
86
62
|
|
|
87
63
|
// Load manifest
|
|
88
64
|
onProgress?.('loading-manifest', 0, 'Loading manifest');
|
|
89
|
-
const manifest = await this.
|
|
65
|
+
const manifest = await this.loadManifestFromAccessors(zipAccessors);
|
|
90
66
|
onProgress?.('loading-manifest', 100, 'Manifest loaded');
|
|
91
67
|
|
|
92
68
|
// Load configuration
|
|
93
69
|
onProgress?.('loading-config', 0, 'Loading configuration');
|
|
94
|
-
const config = await this.
|
|
70
|
+
const config = await this.loadConfigurationFromAccessors(zipAccessors, options);
|
|
95
71
|
onProgress?.('loading-config', 100, 'Configuration loaded');
|
|
96
72
|
|
|
97
73
|
// Extract files and directory structure
|
|
98
74
|
onProgress?.('extracting-files', 0, 'Extracting files');
|
|
99
|
-
const
|
|
75
|
+
const filesList = zipTreeToFiles(fileTree);
|
|
100
76
|
const directory = zipTreeToDirectory(fileTree);
|
|
101
|
-
onProgress?.('extracting-files', 100, `Extracted ${
|
|
77
|
+
onProgress?.('extracting-files', 100, `Extracted ${filesList.length} files`);
|
|
102
78
|
|
|
103
79
|
// Process resources if requested
|
|
104
80
|
let processedResources: ProcessedResources | null = null;
|
|
@@ -114,8 +90,8 @@ export class BrowserZipLoader implements IZipLoader {
|
|
|
114
90
|
} else {
|
|
115
91
|
throw new Error(`Failed to process resources from directory: ${processResult.message}`);
|
|
116
92
|
}
|
|
117
|
-
} else if (
|
|
118
|
-
const processResult = await processImportedFiles(
|
|
93
|
+
} else if (filesList.length > 0) {
|
|
94
|
+
const processResult = await processImportedFiles(filesList, configToUse || undefined);
|
|
119
95
|
if (processResult.isSuccess()) {
|
|
120
96
|
processedResources = processResult.value;
|
|
121
97
|
} else {
|
|
@@ -131,7 +107,7 @@ export class BrowserZipLoader implements IZipLoader {
|
|
|
131
107
|
return succeed({
|
|
132
108
|
manifest,
|
|
133
109
|
config: options.overrideConfig || config,
|
|
134
|
-
files,
|
|
110
|
+
files: filesList,
|
|
135
111
|
directory,
|
|
136
112
|
processedResources
|
|
137
113
|
});
|
|
@@ -168,99 +144,128 @@ export class BrowserZipLoader implements IZipLoader {
|
|
|
168
144
|
}
|
|
169
145
|
|
|
170
146
|
/**
|
|
171
|
-
* Build file tree from
|
|
147
|
+
* Build file tree from ZipFileTreeAccessors (using ts-extras)
|
|
172
148
|
*/
|
|
173
|
-
private async
|
|
149
|
+
private async buildFileTreeFromAccessors(
|
|
150
|
+
zipAccessors: ExtrasZipFileTree.ZipFileTreeAccessors,
|
|
151
|
+
onProgress?: ZipProgressCallback
|
|
152
|
+
): Promise<ZipFileTree> {
|
|
174
153
|
const files = new Map<string, ZipFileItem>();
|
|
175
154
|
const directories = new Set<string>();
|
|
176
155
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
156
|
+
// Get all children from root
|
|
157
|
+
const rootChildrenResult = zipAccessors.getChildren('/');
|
|
158
|
+
if (rootChildrenResult.isFailure()) {
|
|
159
|
+
throw new Error(`Failed to read ZIP contents: ${rootChildrenResult.message}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Process all items recursively
|
|
163
|
+
await this.processFileTreeItems(
|
|
164
|
+
zipAccessors,
|
|
165
|
+
'/',
|
|
166
|
+
rootChildrenResult.value,
|
|
167
|
+
files,
|
|
168
|
+
directories,
|
|
169
|
+
onProgress
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
files,
|
|
174
|
+
directories,
|
|
175
|
+
root: this.findCommonRoot(Array.from(files.keys()))
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Recursively process file tree items
|
|
181
|
+
*/
|
|
182
|
+
private async processFileTreeItems(
|
|
183
|
+
zipAccessors: ExtrasZipFileTree.ZipFileTreeAccessors,
|
|
184
|
+
currentPath: string,
|
|
185
|
+
items: readonly any[],
|
|
186
|
+
files: Map<string, ZipFileItem>,
|
|
187
|
+
directories: Set<string>,
|
|
188
|
+
onProgress?: ZipProgressCallback,
|
|
189
|
+
processed: { count: number } = { count: 0 }
|
|
190
|
+
): Promise<void> {
|
|
191
|
+
for (const item of items) {
|
|
192
|
+
const itemPath = item.absolutePath.startsWith('/') ? item.absolutePath.substring(1) : item.absolutePath;
|
|
193
|
+
|
|
194
|
+
if (item.type === 'directory') {
|
|
195
|
+
directories.add(itemPath);
|
|
196
|
+
files.set(itemPath, {
|
|
197
|
+
name: item.name,
|
|
198
|
+
path: itemPath,
|
|
193
199
|
size: 0,
|
|
194
200
|
isDirectory: true,
|
|
195
|
-
lastModified:
|
|
201
|
+
lastModified: undefined
|
|
196
202
|
});
|
|
197
|
-
} else {
|
|
198
|
-
// Load file content
|
|
199
|
-
const content = await zipEntry.async('string');
|
|
200
203
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
+
// Recursively process children
|
|
205
|
+
const childrenResult = zipAccessors.getChildren(item.absolutePath);
|
|
206
|
+
if (childrenResult.isSuccess()) {
|
|
207
|
+
await this.processFileTreeItems(
|
|
208
|
+
zipAccessors,
|
|
209
|
+
item.absolutePath,
|
|
210
|
+
childrenResult.value,
|
|
211
|
+
files,
|
|
212
|
+
directories,
|
|
213
|
+
onProgress,
|
|
214
|
+
processed
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
} else if (item.type === 'file') {
|
|
218
|
+
// Get file content
|
|
219
|
+
const contentResult = item.getRawContents();
|
|
220
|
+
const content = contentResult.isSuccess() ? contentResult.value : '';
|
|
221
|
+
|
|
222
|
+
files.set(itemPath, {
|
|
223
|
+
name: item.name,
|
|
224
|
+
path: itemPath,
|
|
204
225
|
size: content.length,
|
|
205
226
|
isDirectory: false,
|
|
206
|
-
lastModified:
|
|
227
|
+
lastModified: undefined,
|
|
207
228
|
content
|
|
208
229
|
});
|
|
209
230
|
}
|
|
210
231
|
|
|
211
|
-
processed++;
|
|
212
|
-
|
|
213
|
-
onProgress?.('extracting-files', progress, `Processing ${filename}`);
|
|
232
|
+
processed.count++;
|
|
233
|
+
onProgress?.('extracting-files', processed.count, `Processing ${itemPath}`);
|
|
214
234
|
}
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
files,
|
|
218
|
-
directories,
|
|
219
|
-
root: this.findCommonRoot(Array.from(files.keys()))
|
|
220
|
-
};
|
|
221
235
|
}
|
|
222
236
|
|
|
223
237
|
/**
|
|
224
|
-
* Load manifest from ZIP
|
|
238
|
+
* Load manifest from ZIP using ZipFileTreeAccessors
|
|
225
239
|
*/
|
|
226
|
-
private async
|
|
227
|
-
|
|
228
|
-
|
|
240
|
+
private async loadManifestFromAccessors(
|
|
241
|
+
zipAccessors: ExtrasZipFileTree.ZipFileTreeAccessors
|
|
242
|
+
): Promise<any> {
|
|
243
|
+
const manifestResult = zipAccessors.getFileContents('manifest.json');
|
|
244
|
+
if (manifestResult.isFailure()) {
|
|
229
245
|
return null;
|
|
230
246
|
}
|
|
231
247
|
|
|
232
|
-
const
|
|
233
|
-
console.warn('Failed to read manifest file:', error);
|
|
234
|
-
return null;
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
if (!manifestData) return null;
|
|
238
|
-
|
|
239
|
-
const parseResult = parseManifest(manifestData);
|
|
248
|
+
const parseResult = parseManifest(manifestResult.value);
|
|
240
249
|
return parseResult.orDefault() ?? null;
|
|
241
250
|
}
|
|
242
251
|
|
|
243
252
|
/**
|
|
244
|
-
* Load configuration from ZIP
|
|
253
|
+
* Load configuration from ZIP using ZipFileTreeAccessors
|
|
245
254
|
*/
|
|
246
|
-
private async
|
|
255
|
+
private async loadConfigurationFromAccessors(
|
|
256
|
+
zipAccessors: ExtrasZipFileTree.ZipFileTreeAccessors,
|
|
257
|
+
options: ZipLoadOptions
|
|
258
|
+
): Promise<any> {
|
|
247
259
|
if (options.overrideConfig) {
|
|
248
260
|
return options.overrideConfig;
|
|
249
261
|
}
|
|
250
262
|
|
|
251
|
-
const
|
|
252
|
-
if (
|
|
263
|
+
const configResult = zipAccessors.getFileContents('config.json');
|
|
264
|
+
if (configResult.isFailure()) {
|
|
253
265
|
return null;
|
|
254
266
|
}
|
|
255
267
|
|
|
256
|
-
const
|
|
257
|
-
console.warn('Failed to read config file:', error);
|
|
258
|
-
return null;
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
if (!configData) return null;
|
|
262
|
-
|
|
263
|
-
const parseResult = parseConfiguration(configData);
|
|
268
|
+
const parseResult = parseConfiguration(configResult.value);
|
|
264
269
|
return parseResult.orDefault() ?? null;
|
|
265
270
|
}
|
|
266
271
|
|