@fgv/ts-res-ui-components 5.0.0-10
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/.rush/temp/03c8b056281d9db0a97d8a6e25eea798a160d393.tar.log +271 -0
- package/.rush/temp/chunked-rush-logs/ts-res-ui-components.build.chunks.jsonl +9 -0
- package/.rush/temp/operation/build/all.log +9 -0
- package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
- package/.rush/temp/operation/build/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +1111 -0
- package/README.md +18 -0
- package/REFACTORING_PLAN.md +171 -0
- package/config/jest.config.json +16 -0
- package/config/jest.setup.js +64 -0
- package/config/rig.json +16 -0
- package/lib/components/common/QualifierContextControl.d.ts +14 -0
- package/lib/components/common/QualifierContextControl.d.ts.map +1 -0
- package/lib/components/common/QualifierContextControl.js +78 -0
- package/lib/components/common/QualifierContextControl.js.map +1 -0
- package/lib/components/common/ResourceListView.d.ts +11 -0
- package/lib/components/common/ResourceListView.d.ts.map +1 -0
- package/lib/components/common/ResourceListView.js +20 -0
- package/lib/components/common/ResourceListView.js.map +1 -0
- package/lib/components/common/ResourceTreeView.d.ts +12 -0
- package/lib/components/common/ResourceTreeView.d.ts.map +1 -0
- package/lib/components/common/ResourceTreeView.js +162 -0
- package/lib/components/common/ResourceTreeView.js.map +1 -0
- package/lib/components/forms/HierarchyEditor.d.ts +10 -0
- package/lib/components/forms/HierarchyEditor.d.ts.map +1 -0
- package/lib/components/forms/HierarchyEditor.js +106 -0
- package/lib/components/forms/HierarchyEditor.js.map +1 -0
- package/lib/components/forms/QualifierEditForm.d.ts +11 -0
- package/lib/components/forms/QualifierEditForm.d.ts.map +1 -0
- package/lib/components/forms/QualifierEditForm.js +181 -0
- package/lib/components/forms/QualifierEditForm.js.map +1 -0
- package/lib/components/forms/QualifierTypeEditForm.d.ts +10 -0
- package/lib/components/forms/QualifierTypeEditForm.d.ts.map +1 -0
- package/lib/components/forms/QualifierTypeEditForm.js +172 -0
- package/lib/components/forms/QualifierTypeEditForm.js.map +1 -0
- package/lib/components/forms/ResourceTypeEditForm.d.ts +10 -0
- package/lib/components/forms/ResourceTypeEditForm.d.ts.map +1 -0
- package/lib/components/forms/ResourceTypeEditForm.js +188 -0
- package/lib/components/forms/ResourceTypeEditForm.js.map +1 -0
- package/lib/components/forms/index.d.ts +9 -0
- package/lib/components/forms/index.d.ts.map +1 -0
- package/lib/components/forms/index.js +5 -0
- package/lib/components/forms/index.js.map +1 -0
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts +14 -0
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts.map +1 -0
- package/lib/components/orchestrator/ResourceOrchestrator.js +278 -0
- package/lib/components/orchestrator/ResourceOrchestrator.js.map +1 -0
- package/lib/components/views/CompiledView/index.d.ts +5 -0
- package/lib/components/views/CompiledView/index.d.ts.map +1 -0
- package/lib/components/views/CompiledView/index.js +595 -0
- package/lib/components/views/CompiledView/index.js.map +1 -0
- package/lib/components/views/ConfigurationView/index.d.ts +5 -0
- package/lib/components/views/ConfigurationView/index.d.ts.map +1 -0
- package/lib/components/views/ConfigurationView/index.js +363 -0
- package/lib/components/views/ConfigurationView/index.js.map +1 -0
- package/lib/components/views/FilterView/index.d.ts +5 -0
- package/lib/components/views/FilterView/index.d.ts.map +1 -0
- package/lib/components/views/FilterView/index.js +463 -0
- package/lib/components/views/FilterView/index.js.map +1 -0
- package/lib/components/views/ImportView/index.d.ts +5 -0
- package/lib/components/views/ImportView/index.d.ts.map +1 -0
- package/lib/components/views/ImportView/index.js +514 -0
- package/lib/components/views/ImportView/index.js.map +1 -0
- package/lib/components/views/ResolutionView/EditableJsonView.d.ts +21 -0
- package/lib/components/views/ResolutionView/EditableJsonView.d.ts.map +1 -0
- package/lib/components/views/ResolutionView/EditableJsonView.js +109 -0
- package/lib/components/views/ResolutionView/EditableJsonView.js.map +1 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts +19 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts.map +1 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.js +82 -0
- package/lib/components/views/ResolutionView/ResolutionEditControls.js.map +1 -0
- package/lib/components/views/ResolutionView/index.d.ts +5 -0
- package/lib/components/views/ResolutionView/index.d.ts.map +1 -0
- package/lib/components/views/ResolutionView/index.js +255 -0
- package/lib/components/views/ResolutionView/index.js.map +1 -0
- package/lib/components/views/SourceView/index.d.ts +5 -0
- package/lib/components/views/SourceView/index.d.ts.map +1 -0
- package/lib/components/views/SourceView/index.js +316 -0
- package/lib/components/views/SourceView/index.js.map +1 -0
- package/lib/components/views/ZipLoaderView/index.d.ts +5 -0
- package/lib/components/views/ZipLoaderView/index.d.ts.map +1 -0
- package/lib/components/views/ZipLoaderView/index.js +313 -0
- package/lib/components/views/ZipLoaderView/index.js.map +1 -0
- package/lib/hooks/useConfigurationState.d.ts +46 -0
- package/lib/hooks/useConfigurationState.d.ts.map +1 -0
- package/lib/hooks/useConfigurationState.js +239 -0
- package/lib/hooks/useConfigurationState.js.map +1 -0
- package/lib/hooks/useFilterState.d.ts +7 -0
- package/lib/hooks/useFilterState.d.ts.map +1 -0
- package/lib/hooks/useFilterState.js +80 -0
- package/lib/hooks/useFilterState.js.map +1 -0
- package/lib/hooks/useResolutionState.d.ts +8 -0
- package/lib/hooks/useResolutionState.d.ts.map +1 -0
- package/lib/hooks/useResolutionState.js +253 -0
- package/lib/hooks/useResolutionState.js.map +1 -0
- package/lib/hooks/useResourceData.d.ts +19 -0
- package/lib/hooks/useResourceData.d.ts.map +1 -0
- package/lib/hooks/useResourceData.js +368 -0
- package/lib/hooks/useResourceData.js.map +1 -0
- package/lib/hooks/useViewState.d.ts +10 -0
- package/lib/hooks/useViewState.d.ts.map +1 -0
- package/lib/hooks/useViewState.js +29 -0
- package/lib/hooks/useViewState.js.map +1 -0
- package/lib/index.d.ts +27 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +34 -0
- package/lib/index.js.map +1 -0
- package/lib/test/helpers/testDataLoader.d.ts +37 -0
- package/lib/test/helpers/testDataLoader.d.ts.map +1 -0
- package/lib/test/helpers/testDataLoader.js +171 -0
- package/lib/test/helpers/testDataLoader.js.map +1 -0
- package/lib/test/unit/utils/configurationUtils.test.d.ts +2 -0
- package/lib/test/unit/utils/configurationUtils.test.d.ts.map +1 -0
- package/lib/test/unit/utils/configurationUtils.test.js +497 -0
- package/lib/test/unit/utils/configurationUtils.test.js.map +1 -0
- package/lib/test/unit/utils/fileProcessing.test.d.ts +2 -0
- package/lib/test/unit/utils/fileProcessing.test.d.ts.map +1 -0
- package/lib/test/unit/utils/fileProcessing.test.js +321 -0
- package/lib/test/unit/utils/fileProcessing.test.js.map +1 -0
- package/lib/test/unit/utils/filterResources.test.d.ts +2 -0
- package/lib/test/unit/utils/filterResources.test.d.ts.map +1 -0
- package/lib/test/unit/utils/filterResources.test.js +403 -0
- package/lib/test/unit/utils/filterResources.test.js.map +1 -0
- package/lib/test/unit/utils/resolutionEditing.test.d.ts +2 -0
- package/lib/test/unit/utils/resolutionEditing.test.d.ts.map +1 -0
- package/lib/test/unit/utils/resolutionEditing.test.js +439 -0
- package/lib/test/unit/utils/resolutionEditing.test.js.map +1 -0
- package/lib/test/unit/utils/resolutionUtils.test.d.ts +2 -0
- package/lib/test/unit/utils/resolutionUtils.test.d.ts.map +1 -0
- package/lib/test/unit/utils/resolutionUtils.test.js +397 -0
- package/lib/test/unit/utils/resolutionUtils.test.js.map +1 -0
- package/lib/test/unit/utils/tsResIntegration.test.d.ts +2 -0
- package/lib/test/unit/utils/tsResIntegration.test.d.ts.map +1 -0
- package/lib/test/unit/utils/tsResIntegration.test.js +376 -0
- package/lib/test/unit/utils/tsResIntegration.test.js.map +1 -0
- package/lib/types/index.d.ts +251 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +2 -0
- package/lib/types/index.js.map +1 -0
- package/lib/utils/configurationUtils.d.ts +74 -0
- package/lib/utils/configurationUtils.d.ts.map +1 -0
- package/lib/utils/configurationUtils.js +359 -0
- package/lib/utils/configurationUtils.js.map +1 -0
- package/lib/utils/fileProcessing.d.ts +18 -0
- package/lib/utils/fileProcessing.d.ts.map +1 -0
- package/lib/utils/fileProcessing.js +142 -0
- package/lib/utils/fileProcessing.js.map +1 -0
- package/lib/utils/filterResources.d.ts +38 -0
- package/lib/utils/filterResources.d.ts.map +1 -0
- package/lib/utils/filterResources.js +153 -0
- package/lib/utils/filterResources.js.map +1 -0
- package/lib/utils/resolutionEditing.d.ts +58 -0
- package/lib/utils/resolutionEditing.d.ts.map +1 -0
- package/lib/utils/resolutionEditing.js +246 -0
- package/lib/utils/resolutionEditing.js.map +1 -0
- package/lib/utils/resolutionUtils.d.ts +28 -0
- package/lib/utils/resolutionUtils.d.ts.map +1 -0
- package/lib/utils/resolutionUtils.js +216 -0
- package/lib/utils/resolutionUtils.js.map +1 -0
- package/lib/utils/tsResIntegration.d.ts +71 -0
- package/lib/utils/tsResIntegration.d.ts.map +1 -0
- package/lib/utils/tsResIntegration.js +294 -0
- package/lib/utils/tsResIntegration.js.map +1 -0
- package/lib/utils/zipLoader/browserZipLoader.d.ts +48 -0
- package/lib/utils/zipLoader/browserZipLoader.d.ts.map +1 -0
- package/lib/utils/zipLoader/browserZipLoader.js +247 -0
- package/lib/utils/zipLoader/browserZipLoader.js.map +1 -0
- package/lib/utils/zipLoader/index.d.ts +8 -0
- package/lib/utils/zipLoader/index.d.ts.map +1 -0
- package/lib/utils/zipLoader/index.js +13 -0
- package/lib/utils/zipLoader/index.js.map +1 -0
- package/lib/utils/zipLoader/nodeZipBuilder.d.ts +55 -0
- package/lib/utils/zipLoader/nodeZipBuilder.d.ts.map +1 -0
- package/lib/utils/zipLoader/nodeZipBuilder.js +98 -0
- package/lib/utils/zipLoader/nodeZipBuilder.js.map +1 -0
- package/lib/utils/zipLoader/types.d.ts +139 -0
- package/lib/utils/zipLoader/types.d.ts.map +1 -0
- package/lib/utils/zipLoader/types.js +2 -0
- package/lib/utils/zipLoader/types.js.map +1 -0
- package/lib/utils/zipLoader/zipUtils.d.ts +53 -0
- package/lib/utils/zipLoader/zipUtils.d.ts.map +1 -0
- package/lib/utils/zipLoader/zipUtils.js +229 -0
- package/lib/utils/zipLoader/zipUtils.js.map +1 -0
- package/package.json +69 -0
- package/rush-logs/ts-res-ui-components.build.cache.log +3 -0
- package/rush-logs/ts-res-ui-components.build.log +9 -0
- package/src/components/common/QualifierContextControl.tsx +151 -0
- package/src/components/common/ResourceListView.tsx +63 -0
- package/src/components/common/ResourceTreeView.tsx +271 -0
- package/src/components/forms/HierarchyEditor.tsx +204 -0
- package/src/components/forms/QualifierEditForm.tsx +355 -0
- package/src/components/forms/QualifierTypeEditForm.tsx +347 -0
- package/src/components/forms/ResourceTypeEditForm.tsx +331 -0
- package/src/components/forms/index.ts +11 -0
- package/src/components/orchestrator/ResourceOrchestrator.tsx +372 -0
- package/src/components/views/CompiledView/index.tsx +922 -0
- package/src/components/views/ConfigurationView/index.tsx +800 -0
- package/src/components/views/FilterView/index.tsx +825 -0
- package/src/components/views/ImportView/index.tsx +717 -0
- package/src/components/views/ResolutionView/EditableJsonView.tsx +214 -0
- package/src/components/views/ResolutionView/ResolutionEditControls.tsx +170 -0
- package/src/components/views/ResolutionView/index.tsx +591 -0
- package/src/components/views/SourceView/index.tsx +536 -0
- package/src/components/views/ZipLoaderView/index.tsx +485 -0
- package/src/hooks/useConfigurationState.ts +374 -0
- package/src/hooks/useFilterState.ts +97 -0
- package/src/hooks/useResolutionState.ts +355 -0
- package/src/hooks/useResourceData.ts +467 -0
- package/src/hooks/useViewState.ts +44 -0
- package/src/index.ts +45 -0
- package/src/test/helpers/testDataLoader.ts +195 -0
- package/src/test/unit/utils/configurationUtils.test.ts +630 -0
- package/src/test/unit/utils/fileProcessing.test.ts +391 -0
- package/src/test/unit/utils/filterResources.test.ts +574 -0
- package/src/test/unit/utils/resolutionEditing.test.ts +556 -0
- package/src/test/unit/utils/resolutionUtils.test.ts +521 -0
- package/src/test/unit/utils/tsResIntegration.test.ts +433 -0
- package/src/types/index.ts +322 -0
- package/src/utils/configurationUtils.ts +424 -0
- package/src/utils/fileProcessing.ts +160 -0
- package/src/utils/filterResources.ts +206 -0
- package/src/utils/resolutionEditing.ts +319 -0
- package/src/utils/resolutionUtils.ts +289 -0
- package/src/utils/tsResIntegration.ts +440 -0
- package/src/utils/zipLoader/browserZipLoader.ts +319 -0
- package/src/utils/zipLoader/index.ts +26 -0
- package/src/utils/zipLoader/nodeZipBuilder.ts +153 -0
- package/src/utils/zipLoader/types.ts +175 -0
- package/src/utils/zipLoader/zipUtils.ts +266 -0
- package/temp/build/typescript/ts_gZid87Hu.json +1 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { Result, succeed, fail } from '@fgv/ts-utils';
|
|
2
|
+
import {
|
|
3
|
+
IZipLoader,
|
|
4
|
+
ZipLoadOptions,
|
|
5
|
+
ZipLoadResult,
|
|
6
|
+
ZipProgressCallback,
|
|
7
|
+
ZipFileTree,
|
|
8
|
+
ZipFileItem,
|
|
9
|
+
ZipLoadingStage
|
|
10
|
+
} from './types';
|
|
11
|
+
import { parseManifest, parseConfiguration, zipTreeToFiles, zipTreeToDirectory, isZipFile } from './zipUtils';
|
|
12
|
+
import { processImportedFiles, processImportedDirectory } from '../tsResIntegration';
|
|
13
|
+
import { ProcessedResources } from '../../types';
|
|
14
|
+
|
|
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
|
+
/**
|
|
41
|
+
* Browser-based ZIP loader implementation
|
|
42
|
+
*/
|
|
43
|
+
export class BrowserZipLoader implements IZipLoader {
|
|
44
|
+
/**
|
|
45
|
+
* Load ZIP from File object
|
|
46
|
+
*/
|
|
47
|
+
async loadFromFile(
|
|
48
|
+
file: File,
|
|
49
|
+
options: ZipLoadOptions = {},
|
|
50
|
+
onProgress?: ZipProgressCallback
|
|
51
|
+
): Promise<Result<ZipLoadResult>> {
|
|
52
|
+
onProgress?.('reading-file', 0, `Reading file: ${file.name}`);
|
|
53
|
+
|
|
54
|
+
if (!isZipFile(file.name)) {
|
|
55
|
+
return fail(`File ${file.name} is not a ZIP file`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const buffer = await file.arrayBuffer().catch((error) => {
|
|
59
|
+
throw new Error(`Failed to read file: ${error instanceof Error ? error.message : String(error)}`);
|
|
60
|
+
});
|
|
61
|
+
onProgress?.('reading-file', 100, 'File read complete');
|
|
62
|
+
|
|
63
|
+
return this.loadFromBuffer(buffer, options, onProgress);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Load ZIP from ArrayBuffer
|
|
68
|
+
*/
|
|
69
|
+
async loadFromBuffer(
|
|
70
|
+
buffer: ArrayBuffer,
|
|
71
|
+
options: ZipLoadOptions = {},
|
|
72
|
+
onProgress?: ZipProgressCallback
|
|
73
|
+
): Promise<Result<ZipLoadResult>> {
|
|
74
|
+
onProgress?.('parsing-zip', 0, 'Parsing ZIP archive');
|
|
75
|
+
|
|
76
|
+
const JSZipClass = getJSZip();
|
|
77
|
+
const zip = new JSZipClass();
|
|
78
|
+
const loadedZip = await zip.loadAsync(buffer).catch((error: any) => {
|
|
79
|
+
throw new Error(`Failed to parse ZIP: ${error instanceof Error ? error.message : String(error)}`);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
onProgress?.('parsing-zip', 100, 'ZIP archive parsed');
|
|
83
|
+
|
|
84
|
+
// Build file tree
|
|
85
|
+
const fileTree = await this.buildFileTree(loadedZip, onProgress);
|
|
86
|
+
|
|
87
|
+
// Load manifest
|
|
88
|
+
onProgress?.('loading-manifest', 0, 'Loading manifest');
|
|
89
|
+
const manifest = await this.loadManifest(loadedZip);
|
|
90
|
+
onProgress?.('loading-manifest', 100, 'Manifest loaded');
|
|
91
|
+
|
|
92
|
+
// Load configuration
|
|
93
|
+
onProgress?.('loading-config', 0, 'Loading configuration');
|
|
94
|
+
const config = await this.loadConfiguration(loadedZip, options);
|
|
95
|
+
onProgress?.('loading-config', 100, 'Configuration loaded');
|
|
96
|
+
|
|
97
|
+
// Extract files and directory structure
|
|
98
|
+
onProgress?.('extracting-files', 0, 'Extracting files');
|
|
99
|
+
const files = zipTreeToFiles(fileTree);
|
|
100
|
+
const directory = zipTreeToDirectory(fileTree);
|
|
101
|
+
onProgress?.('extracting-files', 100, `Extracted ${files.length} files`);
|
|
102
|
+
|
|
103
|
+
// Process resources if requested
|
|
104
|
+
let processedResources: ProcessedResources | null = null;
|
|
105
|
+
if (options.autoProcessResources) {
|
|
106
|
+
onProgress?.('processing-resources', 0, 'Processing resources');
|
|
107
|
+
|
|
108
|
+
const configToUse = options.overrideConfig || config;
|
|
109
|
+
|
|
110
|
+
if (directory) {
|
|
111
|
+
const processResult = await processImportedDirectory(directory, configToUse || undefined);
|
|
112
|
+
if (processResult.isSuccess()) {
|
|
113
|
+
processedResources = processResult.value;
|
|
114
|
+
} else {
|
|
115
|
+
throw new Error(`Failed to process resources from directory: ${processResult.message}`);
|
|
116
|
+
}
|
|
117
|
+
} else if (files.length > 0) {
|
|
118
|
+
const processResult = await processImportedFiles(files, configToUse || undefined);
|
|
119
|
+
if (processResult.isSuccess()) {
|
|
120
|
+
processedResources = processResult.value;
|
|
121
|
+
} else {
|
|
122
|
+
throw new Error(`Failed to process resources from files: ${processResult.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
onProgress?.('processing-resources', 100, 'Resources processed');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
onProgress?.('complete', 100, 'ZIP loading complete');
|
|
130
|
+
|
|
131
|
+
return succeed({
|
|
132
|
+
manifest,
|
|
133
|
+
config: options.overrideConfig || config,
|
|
134
|
+
files,
|
|
135
|
+
directory,
|
|
136
|
+
processedResources
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Load ZIP from URL
|
|
142
|
+
*/
|
|
143
|
+
async loadFromUrl(
|
|
144
|
+
url: string,
|
|
145
|
+
options: ZipLoadOptions = {},
|
|
146
|
+
onProgress?: ZipProgressCallback
|
|
147
|
+
): Promise<Result<ZipLoadResult>> {
|
|
148
|
+
onProgress?.('reading-file', 0, `Fetching from URL: ${url}`);
|
|
149
|
+
|
|
150
|
+
const response = await fetch(url).catch((error) => {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`Failed to fetch ZIP from URL: ${error instanceof Error ? error.message : String(error)}`
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
return fail(`Failed to fetch ZIP from URL: ${response.status} ${response.statusText}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const buffer = await response.arrayBuffer().catch((error) => {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Failed to read response buffer: ${error instanceof Error ? error.message : String(error)}`
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
onProgress?.('reading-file', 100, 'URL fetch complete');
|
|
166
|
+
|
|
167
|
+
return this.loadFromBuffer(buffer, options, onProgress);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Build file tree from JSZip instance
|
|
172
|
+
*/
|
|
173
|
+
private async buildFileTree(zip: any, onProgress?: ZipProgressCallback): Promise<ZipFileTree> {
|
|
174
|
+
const files = new Map<string, ZipFileItem>();
|
|
175
|
+
const directories = new Set<string>();
|
|
176
|
+
|
|
177
|
+
const zipFiles = Object.keys(zip.files);
|
|
178
|
+
let processed = 0;
|
|
179
|
+
|
|
180
|
+
// Pre-load all file contents for performance
|
|
181
|
+
for (const filename of zipFiles) {
|
|
182
|
+
const zipEntry = zip.files[filename];
|
|
183
|
+
|
|
184
|
+
if (zipEntry.dir) {
|
|
185
|
+
directories.add(filename);
|
|
186
|
+
files.set(filename, {
|
|
187
|
+
name:
|
|
188
|
+
filename
|
|
189
|
+
.split('/')
|
|
190
|
+
.filter((p) => p)
|
|
191
|
+
.pop() || filename,
|
|
192
|
+
path: filename,
|
|
193
|
+
size: 0,
|
|
194
|
+
isDirectory: true,
|
|
195
|
+
lastModified: zipEntry.date
|
|
196
|
+
});
|
|
197
|
+
} else {
|
|
198
|
+
// Load file content
|
|
199
|
+
const content = await zipEntry.async('string');
|
|
200
|
+
|
|
201
|
+
files.set(filename, {
|
|
202
|
+
name: filename.split('/').pop() || filename,
|
|
203
|
+
path: filename,
|
|
204
|
+
size: content.length,
|
|
205
|
+
isDirectory: false,
|
|
206
|
+
lastModified: zipEntry.date,
|
|
207
|
+
content
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
processed++;
|
|
212
|
+
const progress = Math.round((processed / zipFiles.length) * 100);
|
|
213
|
+
onProgress?.('extracting-files', progress, `Processing ${filename}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
files,
|
|
218
|
+
directories,
|
|
219
|
+
root: this.findCommonRoot(Array.from(files.keys()))
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Load manifest from ZIP
|
|
225
|
+
*/
|
|
226
|
+
private async loadManifest(zip: any): Promise<any> {
|
|
227
|
+
const manifestFile = zip.files['manifest.json'];
|
|
228
|
+
if (!manifestFile) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const manifestData = await manifestFile.async('string').catch((error: any) => {
|
|
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);
|
|
240
|
+
return parseResult.orDefault() ?? null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Load configuration from ZIP
|
|
245
|
+
*/
|
|
246
|
+
private async loadConfiguration(zip: any, options: ZipLoadOptions): Promise<any> {
|
|
247
|
+
if (options.overrideConfig) {
|
|
248
|
+
return options.overrideConfig;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const configFile = zip.files['config.json'];
|
|
252
|
+
if (!configFile) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const configData = await configFile.async('string').catch((error: any) => {
|
|
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);
|
|
264
|
+
return parseResult.orDefault() ?? null;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Find common root directory from file paths
|
|
269
|
+
*/
|
|
270
|
+
private findCommonRoot(paths: string[]): string {
|
|
271
|
+
if (paths.length === 0) return '';
|
|
272
|
+
if (paths.length === 1) return paths[0].split('/')[0] || '';
|
|
273
|
+
|
|
274
|
+
const parts = paths[0].split('/');
|
|
275
|
+
let commonLength = 0;
|
|
276
|
+
|
|
277
|
+
for (let i = 0; i < parts.length; i++) {
|
|
278
|
+
const part = parts[i];
|
|
279
|
+
if (paths.every((path) => path.split('/')[i] === part)) {
|
|
280
|
+
commonLength = i + 1;
|
|
281
|
+
} else {
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return parts.slice(0, commonLength).join('/');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Create a new browser ZIP loader instance
|
|
292
|
+
*/
|
|
293
|
+
export function createBrowserZipLoader(): IZipLoader {
|
|
294
|
+
return new BrowserZipLoader();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Convenience function to load ZIP from File with default options
|
|
299
|
+
*/
|
|
300
|
+
export async function loadZipFile(
|
|
301
|
+
file: File,
|
|
302
|
+
options?: ZipLoadOptions,
|
|
303
|
+
onProgress?: ZipProgressCallback
|
|
304
|
+
): Promise<Result<ZipLoadResult>> {
|
|
305
|
+
const loader = createBrowserZipLoader();
|
|
306
|
+
return loader.loadFromFile(file, options, onProgress);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Convenience function to load ZIP from URL with default options
|
|
311
|
+
*/
|
|
312
|
+
export async function loadZipFromUrl(
|
|
313
|
+
url: string,
|
|
314
|
+
options?: ZipLoadOptions,
|
|
315
|
+
onProgress?: ZipProgressCallback
|
|
316
|
+
): Promise<Result<ZipLoadResult>> {
|
|
317
|
+
const loader = createBrowserZipLoader();
|
|
318
|
+
return loader.loadFromUrl(url, options, onProgress);
|
|
319
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Export types
|
|
2
|
+
export * from './types';
|
|
3
|
+
|
|
4
|
+
// Export utilities
|
|
5
|
+
export * from './zipUtils';
|
|
6
|
+
|
|
7
|
+
// Export browser ZIP loader
|
|
8
|
+
export * from './browserZipLoader';
|
|
9
|
+
|
|
10
|
+
// Export Node.js ZIP builder (placeholder)
|
|
11
|
+
export * from './nodeZipBuilder';
|
|
12
|
+
|
|
13
|
+
// Re-export commonly used functions
|
|
14
|
+
export { loadZipFile, loadZipFromUrl, createBrowserZipLoader } from './browserZipLoader';
|
|
15
|
+
|
|
16
|
+
export { prepareZipData, prepareZipDataFromDirectory, createNodeZipBuilder } from './nodeZipBuilder';
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
generateZipFilename,
|
|
20
|
+
parseManifest,
|
|
21
|
+
parseConfiguration,
|
|
22
|
+
zipTreeToFiles,
|
|
23
|
+
zipTreeToDirectory,
|
|
24
|
+
formatFileSize,
|
|
25
|
+
isZipFile
|
|
26
|
+
} from './zipUtils';
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Result, succeed, fail } from '@fgv/ts-utils';
|
|
2
|
+
import { IZipBuilder, ZipArchiveOptions, ZipArchiveResult, ZipManifest } from './types';
|
|
3
|
+
import { ImportedDirectory, ImportedFile } from '../../types';
|
|
4
|
+
import { generateZipFilename, createManifest, sanitizeFilename, normalizePath } from './zipUtils';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Node.js-based ZIP builder implementation
|
|
8
|
+
*
|
|
9
|
+
* Note: This is a placeholder implementation for interface compatibility.
|
|
10
|
+
* The actual Node.js ZIP building functionality should be implemented
|
|
11
|
+
* in a separate Node.js-specific package or tool.
|
|
12
|
+
*/
|
|
13
|
+
export class NodeZipBuilder implements IZipBuilder {
|
|
14
|
+
/**
|
|
15
|
+
* Create ZIP from files
|
|
16
|
+
*/
|
|
17
|
+
async createFromFiles(
|
|
18
|
+
files: ImportedFile[],
|
|
19
|
+
options: ZipArchiveOptions = {}
|
|
20
|
+
): Promise<Result<ZipArchiveResult>> {
|
|
21
|
+
return fail(
|
|
22
|
+
'Node.js ZIP building not implemented in browser library. Use @fgv/ts-res-browser-cli for ZIP creation.'
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create ZIP from directory
|
|
28
|
+
*/
|
|
29
|
+
async createFromDirectory(
|
|
30
|
+
directory: ImportedDirectory,
|
|
31
|
+
options: ZipArchiveOptions = {}
|
|
32
|
+
): Promise<Result<ZipArchiveResult>> {
|
|
33
|
+
return fail(
|
|
34
|
+
'Node.js ZIP building not implemented in browser library. Use @fgv/ts-res-browser-cli for ZIP creation.'
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create ZIP from file system path
|
|
40
|
+
*/
|
|
41
|
+
async createFromPath(path: string, options: ZipArchiveOptions = {}): Promise<Result<ZipArchiveResult>> {
|
|
42
|
+
return fail(
|
|
43
|
+
'Node.js ZIP building not implemented in browser library. Use @fgv/ts-res-browser-cli for ZIP creation.'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a new Node.js ZIP builder instance
|
|
50
|
+
*
|
|
51
|
+
* Note: This returns a placeholder implementation.
|
|
52
|
+
* For actual ZIP building, use the ts-res-browser-cli tool.
|
|
53
|
+
*/
|
|
54
|
+
export function createNodeZipBuilder(): IZipBuilder {
|
|
55
|
+
return new NodeZipBuilder();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Browser-compatible ZIP creation interface
|
|
60
|
+
*
|
|
61
|
+
* This provides a way to prepare ZIP data in the browser,
|
|
62
|
+
* though actual ZIP file creation requires server-side processing
|
|
63
|
+
* or a separate Node.js tool.
|
|
64
|
+
*/
|
|
65
|
+
export interface BrowserZipData {
|
|
66
|
+
files: Array<{
|
|
67
|
+
path: string;
|
|
68
|
+
content: string;
|
|
69
|
+
}>;
|
|
70
|
+
manifest: ZipManifest;
|
|
71
|
+
config?: any;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Prepare ZIP data structure for browser download or server processing
|
|
76
|
+
*/
|
|
77
|
+
export function prepareZipData(
|
|
78
|
+
files: ImportedFile[],
|
|
79
|
+
options: ZipArchiveOptions = {}
|
|
80
|
+
): Result<BrowserZipData> {
|
|
81
|
+
const timestamp = new Date().toISOString();
|
|
82
|
+
const filename = options.filename || 'ts-res-bundle';
|
|
83
|
+
|
|
84
|
+
// Create manifest
|
|
85
|
+
const manifest = createManifest(
|
|
86
|
+
'file',
|
|
87
|
+
'browser-files',
|
|
88
|
+
'files/',
|
|
89
|
+
options.includeConfig ? 'config.json' : undefined
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Prepare file data
|
|
93
|
+
const zipFiles = files.map((file) => ({
|
|
94
|
+
path: normalizePath(file.path || file.name),
|
|
95
|
+
content: file.content
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
// Add manifest
|
|
99
|
+
zipFiles.push({
|
|
100
|
+
path: 'manifest.json',
|
|
101
|
+
content: JSON.stringify(manifest, null, 2)
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Add configuration if provided
|
|
105
|
+
if (options.includeConfig && options.config) {
|
|
106
|
+
zipFiles.push({
|
|
107
|
+
path: 'config.json',
|
|
108
|
+
content: JSON.stringify(options.config, null, 2)
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return succeed({
|
|
113
|
+
files: zipFiles,
|
|
114
|
+
manifest,
|
|
115
|
+
config: options.config
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Prepare ZIP data from directory structure
|
|
121
|
+
*/
|
|
122
|
+
export function prepareZipDataFromDirectory(
|
|
123
|
+
directory: ImportedDirectory,
|
|
124
|
+
options: ZipArchiveOptions = {}
|
|
125
|
+
): Result<BrowserZipData> {
|
|
126
|
+
// Flatten directory to files
|
|
127
|
+
const files: ImportedFile[] = [];
|
|
128
|
+
|
|
129
|
+
const collectFiles = (dir: ImportedDirectory, basePath: string = '') => {
|
|
130
|
+
// Add files from current directory
|
|
131
|
+
dir.files.forEach((file) => {
|
|
132
|
+
files.push({
|
|
133
|
+
...file,
|
|
134
|
+
path: basePath ? `${basePath}/${file.name}` : file.name
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Recursively collect from subdirectories
|
|
139
|
+
if (dir.subdirectories) {
|
|
140
|
+
dir.subdirectories.forEach((subdir) => {
|
|
141
|
+
const subdirPath = basePath ? `${basePath}/${subdir.name}` : subdir.name;
|
|
142
|
+
collectFiles(subdir, subdirPath);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
collectFiles(directory);
|
|
148
|
+
|
|
149
|
+
return prepareZipData(files, {
|
|
150
|
+
...options,
|
|
151
|
+
filename: options.filename || sanitizeFilename(directory.name)
|
|
152
|
+
});
|
|
153
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Result } from '@fgv/ts-utils';
|
|
2
|
+
import { Config } from '@fgv/ts-res';
|
|
3
|
+
import { ImportedDirectory, ImportedFile, ProcessedResources } from '../../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ZIP manifest metadata structure
|
|
7
|
+
*/
|
|
8
|
+
export interface ZipManifest {
|
|
9
|
+
timestamp: string;
|
|
10
|
+
input?: {
|
|
11
|
+
type: 'file' | 'directory';
|
|
12
|
+
originalPath: string;
|
|
13
|
+
archivePath: string;
|
|
14
|
+
};
|
|
15
|
+
config?: {
|
|
16
|
+
type: 'file';
|
|
17
|
+
originalPath: string;
|
|
18
|
+
archivePath: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for creating ZIP archives
|
|
24
|
+
*/
|
|
25
|
+
export interface ZipArchiveOptions {
|
|
26
|
+
/** Output directory for the ZIP file */
|
|
27
|
+
outputDir?: string;
|
|
28
|
+
/** Custom filename (without extension) */
|
|
29
|
+
filename?: string;
|
|
30
|
+
/** Compression level (0-9) */
|
|
31
|
+
compressionLevel?: number;
|
|
32
|
+
/** Include configuration file */
|
|
33
|
+
includeConfig?: boolean;
|
|
34
|
+
/** Custom configuration to include */
|
|
35
|
+
config?: Config.Model.ISystemConfiguration;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Result of ZIP archive creation
|
|
40
|
+
*/
|
|
41
|
+
export interface ZipArchiveResult {
|
|
42
|
+
/** Path to the created ZIP file */
|
|
43
|
+
filePath: string;
|
|
44
|
+
/** Size of the ZIP file in bytes */
|
|
45
|
+
fileSize: number;
|
|
46
|
+
/** Generated manifest */
|
|
47
|
+
manifest: ZipManifest;
|
|
48
|
+
/** Timestamp when created */
|
|
49
|
+
timestamp: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Options for loading ZIP archives
|
|
54
|
+
*/
|
|
55
|
+
export interface ZipLoadOptions {
|
|
56
|
+
/** Whether to auto-apply configuration found in ZIP */
|
|
57
|
+
autoApplyConfig?: boolean;
|
|
58
|
+
/** Whether to auto-process resources after loading */
|
|
59
|
+
autoProcessResources?: boolean;
|
|
60
|
+
/** Custom configuration to use instead of ZIP config */
|
|
61
|
+
overrideConfig?: Config.Model.ISystemConfiguration;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Result of ZIP loading operation
|
|
66
|
+
*/
|
|
67
|
+
export interface ZipLoadResult {
|
|
68
|
+
/** Manifest from the ZIP */
|
|
69
|
+
manifest: ZipManifest | null;
|
|
70
|
+
/** Configuration found in ZIP */
|
|
71
|
+
config: Config.Model.ISystemConfiguration | null;
|
|
72
|
+
/** Loaded files */
|
|
73
|
+
files: ImportedFile[];
|
|
74
|
+
/** Loaded directory structure */
|
|
75
|
+
directory: ImportedDirectory | null;
|
|
76
|
+
/** File tree for direct processing */
|
|
77
|
+
fileTree?: any; // FileTree.FileTree from ts-utils
|
|
78
|
+
/** Processed resources if auto-processing was enabled */
|
|
79
|
+
processedResources: ProcessedResources | null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* ZIP loading progress stages
|
|
84
|
+
*/
|
|
85
|
+
export type ZipLoadingStage =
|
|
86
|
+
| 'reading-file'
|
|
87
|
+
| 'parsing-zip'
|
|
88
|
+
| 'loading-manifest'
|
|
89
|
+
| 'loading-config'
|
|
90
|
+
| 'extracting-files'
|
|
91
|
+
| 'processing-resources'
|
|
92
|
+
| 'complete';
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Progress callback for ZIP operations
|
|
96
|
+
*/
|
|
97
|
+
export interface ZipProgressCallback {
|
|
98
|
+
(stage: ZipLoadingStage, progress: number, message?: string): void;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* ZIP builder interface (Node.js environment)
|
|
103
|
+
*/
|
|
104
|
+
export interface IZipBuilder {
|
|
105
|
+
/**
|
|
106
|
+
* Create ZIP from files
|
|
107
|
+
*/
|
|
108
|
+
createFromFiles(files: ImportedFile[], options?: ZipArchiveOptions): Promise<Result<ZipArchiveResult>>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create ZIP from directory
|
|
112
|
+
*/
|
|
113
|
+
createFromDirectory(
|
|
114
|
+
directory: ImportedDirectory,
|
|
115
|
+
options?: ZipArchiveOptions
|
|
116
|
+
): Promise<Result<ZipArchiveResult>>;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Create ZIP from file system path
|
|
120
|
+
*/
|
|
121
|
+
createFromPath(path: string, options?: ZipArchiveOptions): Promise<Result<ZipArchiveResult>>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* ZIP loader interface (Browser environment)
|
|
126
|
+
*/
|
|
127
|
+
export interface IZipLoader {
|
|
128
|
+
/**
|
|
129
|
+
* Load ZIP from File object
|
|
130
|
+
*/
|
|
131
|
+
loadFromFile(
|
|
132
|
+
file: File,
|
|
133
|
+
options?: ZipLoadOptions,
|
|
134
|
+
onProgress?: ZipProgressCallback
|
|
135
|
+
): Promise<Result<ZipLoadResult>>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Load ZIP from ArrayBuffer
|
|
139
|
+
*/
|
|
140
|
+
loadFromBuffer(
|
|
141
|
+
buffer: ArrayBuffer,
|
|
142
|
+
options?: ZipLoadOptions,
|
|
143
|
+
onProgress?: ZipProgressCallback
|
|
144
|
+
): Promise<Result<ZipLoadResult>>;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Load ZIP from URL
|
|
148
|
+
*/
|
|
149
|
+
loadFromUrl(
|
|
150
|
+
url: string,
|
|
151
|
+
options?: ZipLoadOptions,
|
|
152
|
+
onProgress?: ZipProgressCallback
|
|
153
|
+
): Promise<Result<ZipLoadResult>>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* File item within a ZIP archive
|
|
158
|
+
*/
|
|
159
|
+
export interface ZipFileItem {
|
|
160
|
+
name: string;
|
|
161
|
+
path: string;
|
|
162
|
+
size: number;
|
|
163
|
+
isDirectory: boolean;
|
|
164
|
+
lastModified?: Date;
|
|
165
|
+
content?: string | ArrayBuffer;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* ZIP file tree representation
|
|
170
|
+
*/
|
|
171
|
+
export interface ZipFileTree {
|
|
172
|
+
files: Map<string, ZipFileItem>;
|
|
173
|
+
directories: Set<string>;
|
|
174
|
+
root: string;
|
|
175
|
+
}
|