mnfst 0.5.75 → 0.5.78
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/lib/manifest.export.js +55 -37
- package/package.json +1 -1
package/lib/manifest.export.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*
|
|
27
27
|
* Supported formats: pdf (default), png, jpeg, webp, csv, json.
|
|
28
28
|
*
|
|
29
|
-
* Library dependencies (
|
|
29
|
+
* Library dependencies (modern-screenshot, jsPDF) are loaded lazily on first
|
|
30
30
|
* use from jsDelivr; pages that never export pay nothing.
|
|
31
31
|
*
|
|
32
32
|
* Elements with `data-no-export` are excluded from visual snapshots.
|
|
@@ -151,12 +151,12 @@ function initializeExportPlugin() {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
// Transparent 1×1 PNG, used as a fallback when an inline image fails to fetch.
|
|
154
|
-
//
|
|
154
|
+
// Without it, a single CORS or 404 image would reject the entire snapshot.
|
|
155
155
|
const TRANSPARENT_PIXEL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=';
|
|
156
156
|
|
|
157
|
-
//
|
|
158
|
-
// SVG fails to decode (oversized clones, malformed inline assets,
|
|
159
|
-
// Translate those into something readable before logging.
|
|
157
|
+
// The snapshot library rejects with the raw `image.onerror` Event when the
|
|
158
|
+
// assembled SVG fails to decode (oversized clones, malformed inline assets,
|
|
159
|
+
// etc.). Translate those into something readable before logging.
|
|
160
160
|
function describeExportError(err) {
|
|
161
161
|
if (err && err.message) return err;
|
|
162
162
|
if (err && typeof Event !== 'undefined' && err instanceof Event) {
|
|
@@ -208,36 +208,41 @@ function initializeExportPlugin() {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
function snapshotOptions(opts) {
|
|
211
|
-
// Filter callback for
|
|
211
|
+
// Filter callback for modern-screenshot — exclude opt-out elements.
|
|
212
212
|
const filter = (node) => {
|
|
213
213
|
if (!node || node.nodeType !== 1) return true;
|
|
214
214
|
return !(node.hasAttribute && node.hasAttribute('data-no-export'));
|
|
215
215
|
};
|
|
216
216
|
const out = {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
217
|
+
scale: Number(opts.resolution) > 0 ? Number(opts.resolution) : 2,
|
|
218
|
+
filter,
|
|
219
|
+
fetch: {
|
|
220
|
+
// Cross-origin assets without CORS headers fall back to this
|
|
221
|
+
// transparent pixel instead of rejecting the entire snapshot.
|
|
222
|
+
placeholderImage: TRANSPARENT_PIXEL,
|
|
223
|
+
// Always bypass the cache so we get fresh, CORS-correct copies.
|
|
224
|
+
bypassingCache: true,
|
|
225
|
+
},
|
|
224
226
|
};
|
|
225
227
|
if (opts.backgroundColor) out.backgroundColor = opts.backgroundColor;
|
|
226
|
-
if (opts.width) out.
|
|
227
|
-
if (opts.height) out.
|
|
228
|
+
if (opts.width) out.width = Number(opts.width);
|
|
229
|
+
if (opts.height) out.height = Number(opts.height);
|
|
228
230
|
return out;
|
|
229
231
|
}
|
|
230
232
|
|
|
231
|
-
// Run
|
|
232
|
-
// failed to decode
|
|
233
|
-
//
|
|
234
|
-
// recover on the second attempt.
|
|
233
|
+
// Run a modern-screenshot call once. If it rejects with a raw Event (the
|
|
234
|
+
// assembled SVG failed to decode), retry once with safer options before
|
|
235
|
+
// propagating. Most real-world failures recover on the second attempt.
|
|
235
236
|
async function snapshotWithFallback(lib, fn, target, so) {
|
|
236
237
|
try {
|
|
237
238
|
return await lib[fn](target, so);
|
|
238
239
|
} catch (err) {
|
|
239
240
|
if (err && err.message) throw err; // a real Error — don't mask it
|
|
240
|
-
const safer = {
|
|
241
|
+
const safer = {
|
|
242
|
+
...so,
|
|
243
|
+
scale: Math.min(so.scale || 2, 1),
|
|
244
|
+
font: false,
|
|
245
|
+
};
|
|
241
246
|
return await lib[fn](target, safer);
|
|
242
247
|
}
|
|
243
248
|
}
|
|
@@ -263,21 +268,22 @@ function initializeExportPlugin() {
|
|
|
263
268
|
}
|
|
264
269
|
|
|
265
270
|
async function exportImage(opts, filename, ext) {
|
|
266
|
-
const lib = await
|
|
271
|
+
const lib = await loadSnapshotLib();
|
|
267
272
|
const target = resolveTarget(opts);
|
|
268
273
|
const so = applySafeDimensions(target, snapshotOptions(opts));
|
|
269
274
|
let dataUrl;
|
|
270
|
-
if (ext === 'png') dataUrl = await snapshotWithFallback(lib, '
|
|
275
|
+
if (ext === 'png') dataUrl = await snapshotWithFallback(lib, 'domToPng', target, so);
|
|
271
276
|
else if (ext === 'jpeg') {
|
|
272
277
|
if (!so.backgroundColor) so.backgroundColor = effectivePageBackground();
|
|
273
278
|
so.quality = Number(opts.quality) > 0 && Number(opts.quality) <= 1
|
|
274
279
|
? Number(opts.quality)
|
|
275
280
|
: 0.95;
|
|
276
|
-
dataUrl = await snapshotWithFallback(lib, '
|
|
281
|
+
dataUrl = await snapshotWithFallback(lib, 'domToJpeg', target, so);
|
|
277
282
|
} else if (ext === 'webp') {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
283
|
+
so.quality = Number(opts.quality) > 0 && Number(opts.quality) <= 1
|
|
284
|
+
? Number(opts.quality)
|
|
285
|
+
: 0.95;
|
|
286
|
+
dataUrl = await snapshotWithFallback(lib, 'domToWebp', target, so);
|
|
281
287
|
}
|
|
282
288
|
triggerDownload(dataUrl, filename);
|
|
283
289
|
}
|
|
@@ -287,17 +293,17 @@ function initializeExportPlugin() {
|
|
|
287
293
|
// own @media print CSS — far more reliable than rasterizing a long page
|
|
288
294
|
// and embedding it as a single image. The user sees the print dialog and
|
|
289
295
|
// picks "Save as PDF" (or any installed PDF printer). Element-scoped PDFs
|
|
290
|
-
// continue to use
|
|
296
|
+
// continue to use modern-screenshot + jsPDF, which is well-behaved at small
|
|
291
297
|
// target sizes.
|
|
292
298
|
async function exportPdf(opts, filename) {
|
|
293
299
|
if (!opts.target) {
|
|
294
300
|
return printToPdf(filename);
|
|
295
301
|
}
|
|
296
|
-
const [imgLib, jsPDFCtor] = await Promise.all([
|
|
302
|
+
const [imgLib, jsPDFCtor] = await Promise.all([loadSnapshotLib(), loadJsPDF()]);
|
|
297
303
|
const target = resolveTarget(opts);
|
|
298
304
|
const so = applySafeDimensions(target, snapshotOptions(opts));
|
|
299
305
|
if (!so.backgroundColor) so.backgroundColor = effectivePageBackground();
|
|
300
|
-
const dataUrl = await snapshotWithFallback(imgLib, '
|
|
306
|
+
const dataUrl = await snapshotWithFallback(imgLib, 'domToPng', target, so);
|
|
301
307
|
const img = await loadImage(dataUrl);
|
|
302
308
|
const orientation = img.width > img.height ? 'landscape' : 'portrait';
|
|
303
309
|
const pdf = new jsPDFCtor({ orientation, unit: 'pt', format: opts.pageSize || 'a4' });
|
|
@@ -423,15 +429,27 @@ function initializeExportPlugin() {
|
|
|
423
429
|
|
|
424
430
|
// Lazy library loaders — cached promises.
|
|
425
431
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
432
|
+
// modern-screenshot is an actively maintained TypeScript rewrite of
|
|
433
|
+
// html-to-image with proper CSS AST parsing. It handles @layer rules,
|
|
434
|
+
// CSS custom properties, modern color functions, and url(data:...) values
|
|
435
|
+
// inside cross-origin stylesheets — all of which html-to-image mishandles.
|
|
436
|
+
// It ships ESM-only, so we load via dynamic import() rather than a script
|
|
437
|
+
// tag. jsDelivr serves the ESM bundle directly.
|
|
438
|
+
let snapshotLibPromise = null;
|
|
439
|
+
function loadSnapshotLib() {
|
|
440
|
+
if (snapshotLibPromise) return snapshotLibPromise;
|
|
441
|
+
snapshotLibPromise = import('https://cdn.jsdelivr.net/npm/modern-screenshot@4/dist/index.mjs')
|
|
442
|
+
.then((mod) => {
|
|
443
|
+
if (!mod || typeof mod.domToPng !== 'function') {
|
|
444
|
+
throw new Error('modern-screenshot failed to load (missing expected exports)');
|
|
445
|
+
}
|
|
446
|
+
return mod;
|
|
447
|
+
})
|
|
448
|
+
.catch((err) => {
|
|
449
|
+
snapshotLibPromise = null; // allow retry on next call
|
|
450
|
+
throw err;
|
|
433
451
|
});
|
|
434
|
-
return
|
|
452
|
+
return snapshotLibPromise;
|
|
435
453
|
}
|
|
436
454
|
|
|
437
455
|
let jsPDFPromise = null;
|