CODE · 2.0 KB
src/export-bundle.js
Workspace snapshot · 08/31 23:51
import JSZip from "jszip";
import { exportHtml, exportThanksHtml } from "./export-html.js";
const extensionFor = (contentType = "") => {
if (contentType.includes("png")) return "png";
if (contentType.includes("webp")) return "webp";
if (contentType.includes("svg")) return "svg";
return "jpg";
};
export async function buildExportBundle({ project, sections, tone, images, publicUrl = "", fetcher = fetch }) {
const zip = new JSZip();
const embedded = [];
const visualSections = (Array.isArray(sections) ? sections : []).filter((section) => section.imageBrief);
for (const [index, image] of (Array.isArray(images) ? images : []).entries()) {
const record = typeof image === "string"
? { src: image, sectionId: visualSections[index]?.id || "" }
: image;
if (!record?.src) continue;
const response = await fetcher(record.src);
if (!response.ok) throw new Error("生成画像を納品ZIPへ保存できませんでした。再生成してからお試しください");
const blob = await response.blob();
const extension = extensionFor(blob.type || response.headers.get("content-type") || "");
const layer = String(record.layer || "content").replace(/[^a-z0-9_-]/giu, "") || "content";
const filename = `images/${record.sectionId || `visual-${index + 1}`}-${layer}.${extension}`;
zip.file(filename, await blob.arrayBuffer());
embedded.push({ ...record, src: filename });
}
const html = exportHtml(project, sections, tone, embedded, { publicUrl });
zip.file("index.html", html);
zip.file("thanks.html", exportThanksHtml(project, tone));
zip.file("README.txt", "index.htmlをブラウザで開いてください。画像はimages/へ同梱済みです。公開前にフォーム送信先、個人情報の取り扱い、計測ID、正式表記を最終確認してください。送信完了ページはthanks.htmlです。\n");
zip.file("design.json", JSON.stringify({ project: { name: project?.name, answers: project?.answers }, tone, sections }, null, 2));
return zip.generateAsync({ type: "blob" });
}