astro-iubenda
Version:
Astro integration for Iubenda Privacy Policy and Terms & Conditions
277 lines (273 loc) • 9.61 kB
JavaScript
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