UNPKG

astro-iubenda

Version:

Astro integration for Iubenda Privacy Policy and Terms & Conditions

277 lines (273 loc) 9.61 kB
import { promises } from 'fs'; import { fileURLToPath } from 'url'; import path from 'path'; // src/utils.ts var PRIVACY_URL = "https://www.iubenda.com/api/privacy-policy/"; var TERMS_URL = "https://www.iubenda.com/api/terms-and-conditions/"; var formatContent = (content, stripMarkup) => stripMarkup ? content.replace(/"|\\/g, "") : content; var PRO_MSG_RE = /convert it to/i; var HTML_TAG_RE = /<\/?(p|div|h\d|ul|ol|li|section|br)\b/i; var buildError = (msg, code) => { const err = new Error(msg); err.code = code; return err; }; var fetchDocument = async (url, stripMarkup) => { const res = await fetch(`${url}/no-markup`); if (!res.ok) { throw buildError(`HTTP ${res.status}`, res.status); } const raw = await res.text(); try { const parsed = JSON.parse(raw); if (typeof parsed.content === "string" && parsed.content.trim()) { return formatContent(parsed.content, stripMarkup); } } catch { } if (PRO_MSG_RE.test(raw)) { throw buildError("Requires Pro plan", 403); } if (raw.length === 0 && !HTML_TAG_RE.test(raw)) { throw buildError("Empty or invalid document", 404); } return formatContent(raw, stripMarkup); }; var handleApiError = (error, name, id, logger) => { const statusCode = error.code || 0; switch (statusCode) { case 404: logger.warn(`${name} for ${id} not found.`); break; case 403: logger.warn(`To access the ${name} for ${id} via API, convert it to Pro.`); break; case 500: logger.warn("Application Error. Please contact info@iubenda.com for support."); break; default: logger.warn(`${name} ${id} \u2014 ${String(error)}`); break; } }; var fetchAllDocuments = async (documentIds, stripMarkup, logger) => { const store = {}; await Promise.all( documentIds.map(async (id) => { const data = { privacyPolicy: null, cookiePolicy: null, termsAndConditions: null }; try { data.privacyPolicy = await fetchDocument(PRIVACY_URL + id, stripMarkup); logger.info(`\u{1F69B} Privacy Policy ${id} fetched`); } catch (e) { handleApiError(e, "Privacy Policy", id, logger); } try { data.cookiePolicy = await fetchDocument(`${PRIVACY_URL + id}/cookie-policy`, stripMarkup); logger.info(`Cookie Policy ${id} fetched`); } catch (e) { handleApiError(e, "Cookie Policy", id, logger); } try { data.termsAndConditions = await fetchDocument(TERMS_URL + id, stripMarkup); logger.info(`Terms & Conditions ${id} fetched`); } catch (e) { handleApiError(e, "Terms & Conditions", id, logger); } store[id] = data; }) ); return store; }; var generateVirtualCode = (store) => ` export const documents = ${JSON.stringify(store, null, 2)}; export const getDocument = (id, type='privacyPolicy') => documents[id]?.[type] ?? null; export default documents;`; var writeDocumentsToJson = async (store, projectRoot, outputDir, logger) => { if (!projectRoot) return; const dir = path.join(fileURLToPath(projectRoot), outputDir); await promises.mkdir(dir, { recursive: true }); await Promise.all( Object.entries(store).map( ([id, data]) => promises.writeFile( path.join(dir, `${id}.json`), JSON.stringify({ documentId: id, ...data }, null, 2) ) ) ); logger.info(`\u{1F4BE} Iubenda JSON-files written to ${path.relative(fileURLToPath(projectRoot), dir)}`); }; var createVitePlugin = (codeOrGetter) => { const getCode = typeof codeOrGetter === "function" ? codeOrGetter : () => codeOrGetter; return { name: "astro-iubenda", resolveId(id) { return id === "virtual:astro-iubenda" ? "\0virtual:astro-iubenda" : void 0; }, load(id) { return id === "\0virtual:astro-iubenda" ? getCode() : void 0; } }; }; // src/cookieFooter.ts var buildCookieFooterScripts = (cookieFooter) => { if (!cookieFooter) return []; const { iubendaOptions, googleTagManagerOptions, injectionStage = "head-inline", bannerVersion = "current" } = cookieFooter; const versionPath = bannerVersion === "current" ? "" : `/${bannerVersion}`; const eventName = typeof googleTagManagerOptions === "object" && googleTagManagerOptions?.eventName ? googleTagManagerOptions.eventName : "iubenda_consent_given"; const dataLayerName = typeof googleTagManagerOptions === "object" && googleTagManagerOptions?.dataLayerName ? googleTagManagerOptions.dataLayerName : "dataLayer"; const gtmCallbacks = googleTagManagerOptions ? ` // GTM events _iub.csConfiguration.onConsentGiven=function(payload){ window.${dataLayerName}=window.${dataLayerName}||[]; window.${dataLayerName}.push({event:'${eventName}'}); if(payload && payload.purposes){ payload.purposes.forEach(function(pid){ window.${dataLayerName}.push({event:'iubenda_consent_given_purpose_'+pid}); }); } }; _iub.csConfiguration.onPreferenceNotNeeded=function(){ window.${dataLayerName}=window.${dataLayerName}||[]; window.${dataLayerName}.push({event:'iubenda_preference_not_needed'}); }; _iub.csConfiguration.onCcpaOptedOut=function(){ window.${dataLayerName}=window.${dataLayerName}||[]; window.${dataLayerName}.push({event:'iubenda_ccpa_opted_out'}); };` : ""; const configSnippet = `var _iub=_iub||[]; _iub.csConfiguration=${JSON.stringify(iubendaOptions)};${gtmCallbacks}`; const addScript = (src, attrs = "") => `(()=>{var s=document.createElement('script');s.src='${src}';s.async=true;${attrs}document.head.appendChild(s);})();`; const siteId = iubendaOptions.siteId; const snippets = [ { stage: injectionStage, code: configSnippet } ]; if (siteId) snippets.push({ stage: injectionStage, code: addScript(`https://cs.iubenda.com/autoblocking/${siteId}.js`) }); snippets.push({ stage: injectionStage, code: addScript("//cdn.iubenda.com/cs/gpp/stub.js") }); if (iubendaOptions.enableTcf) snippets.push({ stage: injectionStage, code: addScript(`//cdn.iubenda.com/cs/tcf${versionPath}/stub-v2.js`) }); if (iubendaOptions.enableUspr) snippets.push({ stage: injectionStage, code: addScript(`//cdn.iubenda.com/cs/gpp${versionPath}/stub.js`) }); snippets.push({ stage: injectionStage, code: addScript(`//cdn.iubenda.com/cs${versionPath}/iubenda_cs.js`, "s.charset='UTF-8';") }); if (googleTagManagerOptions) { const spaSnippet = `document.addEventListener('astro:after-swap',function(){if(window._iub?.cs?.consent_given){window.${dataLayerName}=window.${dataLayerName}||[];window.${dataLayerName}.push({event:'${eventName}'})}});`; snippets.push({ stage: injectionStage, code: spaSnippet }); } return snippets; }; // src/index.ts var VIRTUAL_ID = "\0virtual:astro-iubenda"; function iubenda(opts) { const { documentIds, saveInJson = false, outputDir = "src/content/iubenda", stripMarkup = true, cookieFooter = false } = opts; let projectRoot; let store = {}; let virtualCode = "export default {};"; let viteServer; const refresh = async (providedLogger) => { const logger = providedLogger || viteServer?.config?.logger || console; store = await fetchAllDocuments(documentIds, stripMarkup, logger); virtualCode = generateVirtualCode(store); if (!viteServer) return; if (viteServer.moduleGraph && viteServer.ws) { const mod = viteServer.moduleGraph.getModuleById(VIRTUAL_ID); if (mod) viteServer.moduleGraph.invalidateModule(mod); viteServer.ws.send({ type: "update", updates: [ { type: "js-update", path: "virtual:astro-iubenda", acceptedPath: "virtual:astro-iubenda", timestamp: Date.now() } ] }); } }; const cookieScripts = buildCookieFooterScripts(cookieFooter); return { name: "astro-iubenda", hooks: { "astro:config:setup": ({ config, updateConfig, logger, injectScript }) => { projectRoot = config.root; const vitePlugin = createVitePlugin(() => virtualCode); updateConfig({ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore vite exists in astro config vite: { plugins: [vitePlugin] } }); logger.info("\u2705 virtual:astro-iubenda registered"); for (const script of cookieScripts) { if (script) { const { stage, code } = script; injectScript(stage, code); } logger.info("\u{1F36A} Iubenda Cookie Solution banner injected"); } }, "astro:server:setup": async ({ server, logger }) => { viteServer = server; if (Object.keys(store).length === 0) { await refresh(logger); } const WATCHED = [".env"]; server?.watcher?.add(WATCHED); server?.watcher?.on("change", (p) => { if (!WATCHED.some((f) => p.endsWith(f))) return; logger.info(`\u{1F504} ${p} changed \u2192 refreshing Iubenda docs`); void refresh(logger); }); }, "astro:build:start": async ({ logger }) => { if (Object.keys(store).length === 0) { await refresh(logger); } if (saveInJson) { await writeDocumentsToJson(store, projectRoot, outputDir, logger); } } } }; } export { iubenda as default }; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map