@file-viewer/renderer-archive 2.2.3 → 2.2.4
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/dist/archive.js +10 -3
- package/dist/shapefileBundle.d.ts +16 -0
- package/dist/shapefileBundle.js +94 -0
- package/package.json +4 -3
package/dist/archive.js
CHANGED
|
@@ -4,6 +4,7 @@ import { buildArchiveNestedRenderContext, createArchiveCacheKey, flattenArchiveO
|
|
|
4
4
|
import { readArchiveCache, writeArchiveCache } from './archiveCache.js';
|
|
5
5
|
import { hasLikelyGbkZipFilenames, isLikelyEncryptedArchive, loadArchiveEntriesWithoutWorker, } from './archiveFallback.js';
|
|
6
6
|
import { comicBookStyle, createComicBookController, } from './comicBook.js';
|
|
7
|
+
import { createShapefileBundleArchive, getShapefileBundleEntries, } from './shapefileBundle.js';
|
|
7
8
|
const DEFAULT_MAX_ARCHIVE_SIZE = 320 * 1024 * 1024;
|
|
8
9
|
const DEFAULT_MAX_ENTRY_PREVIEW_SIZE = 64 * 1024 * 1024;
|
|
9
10
|
const DEFAULT_WORKER_TIMEOUT_MS = 30000;
|
|
@@ -829,10 +830,14 @@ export default async function renderArchive(buffer, target, _type, context) {
|
|
|
829
830
|
comicBook.onEntrySelected(entry);
|
|
830
831
|
renderEntryList();
|
|
831
832
|
syncState();
|
|
832
|
-
|
|
833
|
+
const shapefileEntries = getShapefileBundleEntries(entries, entry);
|
|
834
|
+
const previewSize = shapefileEntries.length
|
|
835
|
+
? shapefileEntries.reduce((sum, component) => sum + component.size, 0)
|
|
836
|
+
: entry.size;
|
|
837
|
+
if (previewSize > maxEntryPreviewSize) {
|
|
833
838
|
setError(t('archive.error.entryTooLarge', {
|
|
834
839
|
name: entry.name,
|
|
835
|
-
size: formatArchiveBytes(
|
|
840
|
+
size: formatArchiveBytes(previewSize),
|
|
836
841
|
limit: formatArchiveBytes(maxEntryPreviewSize),
|
|
837
842
|
}));
|
|
838
843
|
return;
|
|
@@ -840,7 +845,9 @@ export default async function renderArchive(buffer, target, _type, context) {
|
|
|
840
845
|
setLoading(true, t('archive.loading.extracting', { name: entry.name }));
|
|
841
846
|
setError('');
|
|
842
847
|
try {
|
|
843
|
-
const entryBuffer =
|
|
848
|
+
const entryBuffer = shapefileEntries.length
|
|
849
|
+
? await createShapefileBundleArchive(shapefileEntries, extractEntryBuffer)
|
|
850
|
+
: await extractEntryBuffer(entry);
|
|
844
851
|
if (requestId !== previewSequence) {
|
|
845
852
|
return;
|
|
846
853
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ArchiveEntryView } from './archiveShared.js';
|
|
2
|
+
/**
|
|
3
|
+
* Finds the files that form the selected Shapefile dataset. Shapefile
|
|
4
|
+
* sidecars are matched case-insensitively by directory and basename so an
|
|
5
|
+
* archive containing multiple datasets never leaks attributes or projection
|
|
6
|
+
* metadata from a neighbouring dataset into the preview.
|
|
7
|
+
*/
|
|
8
|
+
export declare const getShapefileBundleEntries: (entries: readonly ArchiveEntryView[], selectedEntry: ArchiveEntryView) => ArchiveEntryView[];
|
|
9
|
+
/**
|
|
10
|
+
* shpjs accepts an ArrayBuffer ZIP for a complete Shapefile dataset. Archive
|
|
11
|
+
* previews normally extract one entry at a time, so rebuild a small STORE-only
|
|
12
|
+
* ZIP from the selected .shp and its sidecars before delegating to the existing
|
|
13
|
+
* geospatial renderer. STORE avoids wasting CPU recompressing already
|
|
14
|
+
* compressed archive data.
|
|
15
|
+
*/
|
|
16
|
+
export declare const createShapefileBundleArchive: (entries: readonly ArchiveEntryView[], readEntry: (entry: ArchiveEntryView) => Promise<ArrayBuffer>) => Promise<ArrayBuffer>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { getArchiveEntryExtension, } from './archiveShared.js';
|
|
2
|
+
const SHAPEFILE_COMPONENT_EXTENSIONS = new Set([
|
|
3
|
+
'shp',
|
|
4
|
+
'shx',
|
|
5
|
+
'dbf',
|
|
6
|
+
'prj',
|
|
7
|
+
'cpg',
|
|
8
|
+
]);
|
|
9
|
+
const SHAPEFILE_COMPONENT_ORDER = new Map([
|
|
10
|
+
['shp', 0],
|
|
11
|
+
['dbf', 1],
|
|
12
|
+
['shx', 2],
|
|
13
|
+
['prj', 3],
|
|
14
|
+
['cpg', 4],
|
|
15
|
+
]);
|
|
16
|
+
const normalizeArchivePath = (path) => path
|
|
17
|
+
.replace(/^\/+/, '')
|
|
18
|
+
.replace(/\\/g, '/');
|
|
19
|
+
const getArchivePathParts = (path) => {
|
|
20
|
+
const normalized = normalizeArchivePath(path);
|
|
21
|
+
const slash = normalized.lastIndexOf('/');
|
|
22
|
+
const directory = slash === -1 ? '' : normalized.slice(0, slash);
|
|
23
|
+
const filename = slash === -1 ? normalized : normalized.slice(slash + 1);
|
|
24
|
+
const extension = getArchiveEntryExtension(filename);
|
|
25
|
+
const stem = extension
|
|
26
|
+
? filename.slice(0, -(extension.length + 1))
|
|
27
|
+
: filename;
|
|
28
|
+
return {
|
|
29
|
+
directory: directory.toLowerCase(),
|
|
30
|
+
filename,
|
|
31
|
+
stem: stem.toLowerCase(),
|
|
32
|
+
extension,
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
const resolveJSZipWriter = (module) => {
|
|
36
|
+
const record = module;
|
|
37
|
+
const defaultRecord = record === null || record === void 0 ? void 0 : record.default;
|
|
38
|
+
const candidates = [
|
|
39
|
+
record === null || record === void 0 ? void 0 : record.default,
|
|
40
|
+
defaultRecord === null || defaultRecord === void 0 ? void 0 : defaultRecord.default,
|
|
41
|
+
record === null || record === void 0 ? void 0 : record.JSZip,
|
|
42
|
+
module,
|
|
43
|
+
];
|
|
44
|
+
const constructor = candidates.find(candidate => typeof candidate === 'function');
|
|
45
|
+
if (!constructor) {
|
|
46
|
+
throw new Error('JSZip module does not expose a constructor.');
|
|
47
|
+
}
|
|
48
|
+
return constructor;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Finds the files that form the selected Shapefile dataset. Shapefile
|
|
52
|
+
* sidecars are matched case-insensitively by directory and basename so an
|
|
53
|
+
* archive containing multiple datasets never leaks attributes or projection
|
|
54
|
+
* metadata from a neighbouring dataset into the preview.
|
|
55
|
+
*/
|
|
56
|
+
export const getShapefileBundleEntries = (entries, selectedEntry) => {
|
|
57
|
+
const selected = getArchivePathParts(selectedEntry.path);
|
|
58
|
+
if (selected.extension !== 'shp') {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
return entries
|
|
62
|
+
.filter(entry => {
|
|
63
|
+
const candidate = getArchivePathParts(entry.path);
|
|
64
|
+
return candidate.directory === selected.directory &&
|
|
65
|
+
candidate.stem === selected.stem &&
|
|
66
|
+
SHAPEFILE_COMPONENT_EXTENSIONS.has(candidate.extension);
|
|
67
|
+
})
|
|
68
|
+
.sort((left, right) => {
|
|
69
|
+
var _a, _b;
|
|
70
|
+
const leftExtension = getArchivePathParts(left.path).extension;
|
|
71
|
+
const rightExtension = getArchivePathParts(right.path).extension;
|
|
72
|
+
return ((_a = SHAPEFILE_COMPONENT_ORDER.get(leftExtension)) !== null && _a !== void 0 ? _a : Number.MAX_SAFE_INTEGER) -
|
|
73
|
+
((_b = SHAPEFILE_COMPONENT_ORDER.get(rightExtension)) !== null && _b !== void 0 ? _b : Number.MAX_SAFE_INTEGER);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* shpjs accepts an ArrayBuffer ZIP for a complete Shapefile dataset. Archive
|
|
78
|
+
* previews normally extract one entry at a time, so rebuild a small STORE-only
|
|
79
|
+
* ZIP from the selected .shp and its sidecars before delegating to the existing
|
|
80
|
+
* geospatial renderer. STORE avoids wasting CPU recompressing already
|
|
81
|
+
* compressed archive data.
|
|
82
|
+
*/
|
|
83
|
+
export const createShapefileBundleArchive = async (entries, readEntry) => {
|
|
84
|
+
const JSZip = resolveJSZipWriter(await import('jszip'));
|
|
85
|
+
const zip = new JSZip();
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
const { filename } = getArchivePathParts(entry.path);
|
|
88
|
+
zip.file(filename || entry.name, await readEntry(entry));
|
|
89
|
+
}
|
|
90
|
+
return zip.generateAsync({
|
|
91
|
+
type: 'arraybuffer',
|
|
92
|
+
compression: 'STORE',
|
|
93
|
+
});
|
|
94
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@file-viewer/renderer-archive",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Standalone archive renderer plugin for File Viewer with libarchive worker, ZIP/TAR fallback, IndexedDB cache, and nested previews.",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"LICENSE"
|
|
57
57
|
],
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@file-viewer/core": "2.2.
|
|
59
|
+
"@file-viewer/core": "2.2.4",
|
|
60
60
|
"jszip": "^3.10.1",
|
|
61
61
|
"libarchive.js": "^2.0.2"
|
|
62
62
|
},
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "tsc -b tsconfig.json",
|
|
69
69
|
"type-check": "tsc -b tsconfig.json",
|
|
70
|
-
"verify:github-101": "pnpm build && node scripts/verify-github-101.mjs"
|
|
70
|
+
"verify:github-101": "pnpm build && node scripts/verify-github-101.mjs",
|
|
71
|
+
"verify:github-148": "pnpm build && node scripts/verify-github-148.mjs"
|
|
71
72
|
}
|
|
72
73
|
}
|