Merge the Shapefile export fix and the startup-loading fix

Two branches from parallel sessions, merged in that order because the first
removes shp-write and with it JSZip, which the second was working around.

Conflicts resolved by hand in vite.config.js: the JSZip chunk rule the second
branch added is dropped, since nothing reaches JSZip any more — shapefiles are
written by src/shapefile-write.js and shpjs 6 unzips with but-unzip. Vite's
preload-helper rule stays, which is what keeps the geotiff chunk out of the
startup preload. dist/ was rebuilt from scratch rather than merged, its hashed
file names being a conflict on every file.

Service Worker v26 now records both changes.

Verified on the merged tree: a production build preloads only the entry,
vite-helpers, bootstrap, openlayers and ol-ext — geotiff, shpjs and pako are
gone from startup, and Analyse still fetches its chunk and turf on first use.
Shapefile export, driven through the Export GIS modal, writes a zip holding the
point and polygon sets with their .prj and .cpg.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ekke 2026-09-23 13:47:21 +02:00
commit adbf9ba211
24 changed files with 108 additions and 87 deletions

File diff suppressed because one or more lines are too long

12
dist/assets/analysis-modal-CwVHFT_a.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/geotiff-B_Wfowsp.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/index-Ddi_Zl4K.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/assets/pdf-export-DJBZ6C0H.js vendored Normal file
View File

@ -0,0 +1,2 @@
import{E as f,a as b}from"./jspdf-HFui8oS7.js";import"./pako-n3Pgozwg.js";import"./vite-helpers-BlTxHScW.js";b(f);let s=null;async function x(){if(s)return s;try{const e=new Image;e.crossOrigin="anonymous",await new Promise((r,i)=>{e.onload=r,e.onerror=i,e.src="./app-icons/luspa-pdf.jpg"});const n=document.createElement("canvas");n.width=e.naturalWidth,n.height=e.naturalHeight;const t=n.getContext("2d");return t.fillStyle="#ffffff",t.fillRect(0,0,n.width,n.height),t.drawImage(e,0,0),s=n.toDataURL("image/jpeg",.92),s}catch(e){return console.warn("[PDF] Could not load logo:",e),null}}async function D({title:e,rows:n}){const t=new f({orientation:"portrait",unit:"mm",format:"a4"}),r=t.internal.pageSize.getWidth(),i=[30,26,75],g=await x(),c=28,a=14;let o=14;g&&t.addImage(g,"JPEG",a,o,c,c);const m=a+c+6;t.setFont("helvetica","bold"),t.setFontSize(18),t.setTextColor(...i),t.text("LUPMIS",m,o+11),t.setFont("helvetica","normal"),t.setFontSize(12),t.text(e,m,o+19);const d=new Date,h=d.toLocaleDateString(void 0,{year:"numeric",month:"long",day:"numeric"}),p=d.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});t.setFontSize(9),t.setTextColor(120,120,120),t.text(`${h} ${p}`,r-a,o+11,{align:"right"}),o+=c+6,t.setDrawColor(...i),t.setLineWidth(.5),t.line(a,o,r-a,o),o+=6;const y=n.map(l=>[l.label,l.value]);t.autoTable({startY:o,head:[["Property","Value"]],body:y,margin:{left:a,right:a},styles:{font:"helvetica",fontSize:10,cellPadding:4},headStyles:{fillColor:i,textColor:[255,255,255],fontStyle:"bold"},alternateRowStyles:{fillColor:[245,245,250]},columnStyles:{0:{fontStyle:"bold",cellWidth:50}}});const S=t.lastAutoTable.finalY+10;t.setFontSize(8),t.setTextColor(160,160,160),t.text("Generated by LUPMIS2 Land Use Planning & Management Information System",a,S);const w=t.output("blob"),u=URL.createObjectURL(w);if(!window.open(u,"_blank")){const l=document.createElement("a");l.href=u,l.download=`${e.replace(/\s+/g,"_")}_${d.toISOString().slice(0,10)}.pdf`,document.body.appendChild(l),l.click(),document.body.removeChild(l)}}export{D as exportAnalysisPDF};
//# sourceMappingURL=pdf-export-DJBZ6C0H.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,2 +0,0 @@
import{E as f,a as b}from"./jspdf-Dh9Z6DYg.js";import"./pako-n3Pgozwg.js";import"./geotiff-B7h926ms.js";import"./openlayers-D5-VSKzq.js";b(f);let s=null;async function x(){if(s)return s;try{const e=new Image;e.crossOrigin="anonymous",await new Promise((r,i)=>{e.onload=r,e.onerror=i,e.src="./app-icons/luspa-pdf.jpg"});const n=document.createElement("canvas");n.width=e.naturalWidth,n.height=e.naturalHeight;const t=n.getContext("2d");return t.fillStyle="#ffffff",t.fillRect(0,0,n.width,n.height),t.drawImage(e,0,0),s=n.toDataURL("image/jpeg",.92),s}catch(e){return console.warn("[PDF] Could not load logo:",e),null}}async function F({title:e,rows:n}){const t=new f({orientation:"portrait",unit:"mm",format:"a4"}),r=t.internal.pageSize.getWidth(),i=[30,26,75],g=await x(),c=28,a=14;let o=14;g&&t.addImage(g,"JPEG",a,o,c,c);const m=a+c+6;t.setFont("helvetica","bold"),t.setFontSize(18),t.setTextColor(...i),t.text("LUPMIS",m,o+11),t.setFont("helvetica","normal"),t.setFontSize(12),t.text(e,m,o+19);const d=new Date,h=d.toLocaleDateString(void 0,{year:"numeric",month:"long",day:"numeric"}),p=d.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});t.setFontSize(9),t.setTextColor(120,120,120),t.text(`${h} ${p}`,r-a,o+11,{align:"right"}),o+=c+6,t.setDrawColor(...i),t.setLineWidth(.5),t.line(a,o,r-a,o),o+=6;const y=n.map(l=>[l.label,l.value]);t.autoTable({startY:o,head:[["Property","Value"]],body:y,margin:{left:a,right:a},styles:{font:"helvetica",fontSize:10,cellPadding:4},headStyles:{fillColor:i,textColor:[255,255,255],fontStyle:"bold"},alternateRowStyles:{fillColor:[245,245,250]},columnStyles:{0:{fontStyle:"bold",cellWidth:50}}});const S=t.lastAutoTable.finalY+10;t.setFontSize(8),t.setTextColor(160,160,160),t.text("Generated by LUPMIS2 Land Use Planning & Management Information System",a,S);const w=t.output("blob"),u=URL.createObjectURL(w);if(!window.open(u,"_blank")){const l=document.createElement("a");l.href=u,l.download=`${e.replace(/\s+/g,"_")}_${d.toISOString().slice(0,10)}.pdf`,document.body.appendChild(l),l.click(),document.body.removeChild(l)}}export{F as exportAnalysisPDF};
//# sourceMappingURL=pdf-export-cmdWg-4I.js.map

2
dist/assets/vite-helpers-BlTxHScW.js vendored Normal file
View File

@ -0,0 +1,2 @@
const p="modulepreload",v=function(l){return"/"+l},d={},y=function(f,c,E){let i=Promise.resolve();if(c&&c.length>0){let r=function(e){return Promise.all(e.map(o=>Promise.resolve(o).then(s=>({status:"fulfilled",value:s}),s=>({status:"rejected",reason:s}))))};document.getElementsByTagName("link");const t=document.querySelector("meta[property=csp-nonce]"),u=t?.nonce||t?.getAttribute("nonce");i=r(c.map(e=>{if(e=v(e),e in d)return;d[e]=!0;const o=e.endsWith(".css"),s=o?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${e}"]${s}`))return;const n=document.createElement("link");if(n.rel=o?"stylesheet":p,o||(n.as="script"),n.crossOrigin="",n.href=e,u&&n.setAttribute("nonce",u),document.head.appendChild(n),o)return new Promise((m,h)=>{n.addEventListener("load",m),n.addEventListener("error",()=>h(new Error(`Unable to preload CSS for ${e}`)))})}))}function a(r){const t=new Event("vite:preloadError",{cancelable:!0});if(t.payload=r,window.dispatchEvent(t),!t.defaultPrevented)throw r}return i.then(r=>{for(const t of r||[])t.status==="rejected"&&a(t.reason);return f().catch(a)})};export{y as _};
//# sourceMappingURL=vite-helpers-BlTxHScW.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"vite-helpers-BlTxHScW.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}

View File

@ -1,4 +1,4 @@
import{d as f,z as d}from"./index-uzWunVfg.js";import"./geotiff-B7h926ms.js";import"./openlayers-D5-VSKzq.js";import"./pako-n3Pgozwg.js";import"./bootstrap-D1-uvFxm.js";import"./ol-ext-DV70ljNY.js";function p(e){return String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g,"")}function h(e){let l="",o=e;for(;o>=0;)l=String.fromCharCode(o%26+65)+l,o=Math.floor(o/26)-1;return l}function x(e){return e&&!Array.isArray(e)&&Array.isArray(e.cells)?{cells:e.cells,bold:!!e.bold}:{cells:Array.isArray(e)?e:[e],bold:!1}}function u(e){const l=[];return e.forEach((o,n)=>{const{cells:t,bold:a}=x(o),r=a?' s="1"':"",i=t.map((s,c)=>{const m=`${h(c)}${n+1}`;return s==null||s===""?`<c r="${m}"${r}/>`:typeof s=="number"&&Number.isFinite(s)?`<c r="${m}"${r}><v>${s}</v></c>`:`<c r="${m}"${r} t="inlineStr"><is><t xml:space="preserve">${p(s)}</t></is></c>`}).join("");l.push(`<row r="${n+1}">${i}</row>`)}),`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
import{d as f,z as d}from"./index-Ddi_Zl4K.js";import"./vite-helpers-BlTxHScW.js";import"./bootstrap-D1-uvFxm.js";import"./openlayers-D5-VSKzq.js";import"./ol-ext-DV70ljNY.js";function p(e){return String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g,"")}function h(e){let l="",o=e;for(;o>=0;)l=String.fromCharCode(o%26+65)+l,o=Math.floor(o/26)-1;return l}function x(e){return e&&!Array.isArray(e)&&Array.isArray(e.cells)?{cells:e.cells,bold:!!e.bold}:{cells:Array.isArray(e)?e:[e],bold:!1}}function u(e){const l=[];return e.forEach((o,n)=>{const{cells:t,bold:a}=x(o),r=a?' s="1"':"",i=t.map((s,c)=>{const m=`${h(c)}${n+1}`;return s==null||s===""?`<c r="${m}"${r}/>`:typeof s=="number"&&Number.isFinite(s)?`<c r="${m}"${r}><v>${s}</v></c>`:`<c r="${m}"${r} t="inlineStr"><is><t xml:space="preserve">${p(s)}</t></is></c>`}).join("");l.push(`<row r="${n+1}">${i}</row>`)}),`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>${l.join("")}</sheetData></worksheet>`}const y=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><sz val="11"/><name val="Calibri"/></font></fonts>
@ -26,5 +26,5 @@ ${n.map(t=>`<Override PartName="/${t.file}" ContentType="application/vnd.openxml
${n.map(t=>`<Relationship Id="rId${t.id}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${t.id}.xml"/>`).join(`
`)}
<Relationship Id="rIdStyles" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
</Relationships>`,o["xl/styles.xml"]=y,d(o,"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")}function v(e,l){f(g(e),l)}export{g as buildXlsxBlob,v as exportXlsx};
//# sourceMappingURL=xlsx-D1ZOqanH.js.map
</Relationships>`,o["xl/styles.xml"]=y,d(o,"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")}function S(e,l){f(g(e),l)}export{g as buildXlsxBlob,S as exportXlsx};
//# sourceMappingURL=xlsx-CR1gFYlV.js.map

File diff suppressed because one or more lines are too long

9
dist/index.html vendored
View File

@ -1997,14 +1997,13 @@
}
</style>
<script type="module" crossorigin src="/assets/index-uzWunVfg.js"></script>
<link rel="modulepreload" crossorigin href="/assets/openlayers-D5-VSKzq.js">
<link rel="modulepreload" crossorigin href="/assets/pako-n3Pgozwg.js">
<link rel="modulepreload" crossorigin href="/assets/geotiff-B7h926ms.js">
<script type="module" crossorigin src="/assets/index-Ddi_Zl4K.js"></script>
<link rel="modulepreload" crossorigin href="/assets/vite-helpers-BlTxHScW.js">
<link rel="modulepreload" crossorigin href="/assets/bootstrap-D1-uvFxm.js">
<link rel="modulepreload" crossorigin href="/assets/openlayers-D5-VSKzq.js">
<link rel="modulepreload" crossorigin href="/assets/ol-ext-DV70ljNY.js">
<link rel="stylesheet" crossorigin href="/assets/openlayers-BtPuoxOl.css">
<link rel="stylesheet" crossorigin href="/assets/bootstrap-BtmJYOxZ.css">
<link rel="stylesheet" crossorigin href="/assets/openlayers-BtPuoxOl.css">
<link rel="stylesheet" crossorigin href="/assets/ol-ext-BgKrOIxx.css">
<link rel="stylesheet" crossorigin href="/assets/index-Dp-9_Fz_.css">
</head>

7
dist/sw.js vendored
View File

@ -151,7 +151,12 @@
// on `process.browser`, and delivers the zip by navigating to a data:
// URL, which Chromium blocks. src/shapefile-write.js now writes the
// .shp/.shx/.dbf/.prj/.cpg itself and hands over a Blob, so shp-write,
// dbf and JSZip are gone from the dependency tree. New hashed bundle.
// dbf and JSZip are gone from the dependency tree.
// Also: the build no longer preloads the geotiff and shpjs chunks at
// startup. Vite's preload helper had been parked inside the geotiff
// chunk, and JSZip inside the shpjs one, so the entry needed both on
// every visit — about 428 kB that is now fetched only when a COG
// layer or a shapefile import actually asks for it. New hashed bundle.
const CACHE_VERSION = 'v26';
const SHELL_CACHE = `shell-${CACHE_VERSION}`;
const MODULES_CACHE = `modules-${CACHE_VERSION}`;

View File

@ -151,7 +151,12 @@
// on `process.browser`, and delivers the zip by navigating to a data:
// URL, which Chromium blocks. src/shapefile-write.js now writes the
// .shp/.shx/.dbf/.prj/.cpg itself and hands over a Blob, so shp-write,
// dbf and JSZip are gone from the dependency tree. New hashed bundle.
// dbf and JSZip are gone from the dependency tree.
// Also: the build no longer preloads the geotiff and shpjs chunks at
// startup. Vite's preload helper had been parked inside the geotiff
// chunk, and JSZip inside the shpjs one, so the entry needed both on
// every visit — about 428 kB that is now fetched only when a COG
// layer or a shapefile import actually asks for it. New hashed bundle.
const CACHE_VERSION = 'v26';
const SHELL_CACHE = `shell-${CACHE_VERSION}`;
const MODULES_CACHE = `modules-${CACHE_VERSION}`;

View File

@ -50,6 +50,14 @@ export default defineConfig({
// Without its own chunk, Rollup assigns it to one of them and the two
// chunks reference each other ("Circular chunk: jspdf -> geotiff").
if (id.includes('node_modules/pako/')) return 'pako';
// Vite's preload helper (the `__vitePreload` used by every dynamic
// import()) is a virtual module with no node_modules path, so Rollup
// is free to park it in whichever chunk it likes — it used to land in
// the geotiff chunk. The entry needs the helper at startup, so that
// made the browser preload all ~257 kB of geotiff.js on every visit,
// defeating the lazy rule below. Give the helper a chunk of its own
// (~1 kB) so the entry pulls in only that.
if (id.includes('vite/preload-helper')) return 'vite-helpers';
// Turf.js — only needed when the Analysis panel is opened, which
// main.js imports dynamically. Keep it out of the eager bundle.
if (id.includes('node_modules/@turf/')) return 'turf';
@ -63,9 +71,10 @@ export default defineConfig({
if (id.includes('node_modules/ol/')) return 'openlayers';
if (id.includes('node_modules/ol-ext/')) return 'ol-ext';
if (id.includes('node_modules/bootstrap/')) return 'bootstrap';
// shpjs — dynamically imported at runtime, so this chunk is only
// fetched when the user imports a shapefile. (Writing shapefiles
// needs no dependency at all; see src/shapefile-write.js.)
// shpjs — dynamically imported at runtime (main.js), so this chunk is
// only fetched when the user imports a shapefile. Writing shapefiles
// needs no dependency at all (src/shapefile-write.js), and shpjs 6
// unzips with but-unzip, so nothing here reaches JSZip any more.
if (id.includes('node_modules/shpjs/')) return 'shpjs';
if (id.includes('node_modules/jspdf')) return 'jspdf';
},