@bagelink/blox 1.12.22 → 1.12.25

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.
Files changed (59) hide show
  1. package/dist/CmsPageView.vue.d.ts.map +1 -1
  2. package/dist/PreviewApp-C2T03Jm9.cjs +4 -0
  3. package/dist/PreviewApp-CmThrLvv.js +4 -0
  4. package/dist/PreviewApp.vue.d.ts.map +1 -1
  5. package/dist/PreviewApp.vue_vue_type_style_index_0_lang-B0N5QbfS.js +157 -0
  6. package/dist/PreviewApp.vue_vue_type_style_index_0_lang-BTuE4GmT.cjs +156 -0
  7. package/dist/api/index.d.ts.map +1 -1
  8. package/dist/bridge.d.ts +1 -0
  9. package/dist/bridge.d.ts.map +1 -1
  10. package/dist/core-C3Iu5qa2.js +460 -0
  11. package/dist/core-_fnHoEZN.cjs +459 -0
  12. package/dist/core.d.ts.map +1 -1
  13. package/dist/createBloxApp.d.ts +107 -0
  14. package/dist/createBloxApp.d.ts.map +1 -0
  15. package/dist/defineBlock.d.ts +2 -0
  16. package/dist/defineBlock.d.ts.map +1 -1
  17. package/dist/index.cjs +79 -585
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.mjs +78 -583
  21. package/dist/{prerender-6jE_obPj.cjs → prerender-Bi7YtzSp.cjs} +246 -6
  22. package/dist/prerender-D3Q4jKXm.js +522 -0
  23. package/dist/schema.d.ts +2 -0
  24. package/dist/schema.d.ts.map +1 -1
  25. package/dist/ssg/cli.cjs +48 -5
  26. package/dist/ssg/cli.mjs +48 -5
  27. package/dist/ssg/client.cjs +50 -3
  28. package/dist/ssg/client.d.ts +2 -1
  29. package/dist/ssg/client.d.ts.map +1 -1
  30. package/dist/ssg/client.mjs +49 -2
  31. package/dist/ssg/cms-routes.d.ts +21 -4
  32. package/dist/ssg/cms-routes.d.ts.map +1 -1
  33. package/dist/ssg/collection-cache.d.ts +53 -0
  34. package/dist/ssg/collection-cache.d.ts.map +1 -0
  35. package/dist/ssg/constants.d.ts +4 -0
  36. package/dist/ssg/constants.d.ts.map +1 -1
  37. package/dist/ssg/createSSREntry.d.ts +73 -0
  38. package/dist/ssg/createSSREntry.d.ts.map +1 -0
  39. package/dist/ssg/index.cjs +138 -6
  40. package/dist/ssg/index.d.ts +10 -3
  41. package/dist/ssg/index.d.ts.map +1 -1
  42. package/dist/ssg/index.mjs +124 -12
  43. package/dist/ssg/prerender.d.ts +19 -1
  44. package/dist/ssg/prerender.d.ts.map +1 -1
  45. package/dist/ssg/render-resolved-page.d.ts +13 -3
  46. package/dist/ssg/render-resolved-page.d.ts.map +1 -1
  47. package/dist/ssg/seo.d.ts +66 -0
  48. package/dist/ssg/seo.d.ts.map +1 -0
  49. package/dist/style.css +20 -0
  50. package/dist/vite-plugin.cjs +142 -3
  51. package/dist/vite-plugin.d.ts +22 -21
  52. package/dist/vite-plugin.d.ts.map +1 -1
  53. package/dist/vite-plugin.mjs +142 -3
  54. package/package.json +4 -1
  55. package/dist/PreviewApp-BZNzZkit.js +0 -4
  56. package/dist/PreviewApp-C1WvJWI4.cjs +0 -4
  57. package/dist/constants-BIbQhd3z.js +0 -4
  58. package/dist/constants-fZvybj0k.cjs +0 -3
  59. package/dist/prerender-DYmDaqcz.js +0 -282
@@ -25,18 +25,44 @@ const fs = require("node:fs/promises");
25
25
  const path = require("node:path");
26
26
  const process = require("node:process");
27
27
  const node_url = require("node:url");
28
- async function fetchCmsPrerenderPaths(apiBase, websiteName) {
28
+ async function fetchCmsSiteData(apiBase, websiteName) {
29
29
  const sitesRes = await fetch(`${apiBase}/cms/websites`);
30
30
  const sitesData = await sitesRes.json();
31
31
  const sites = Array.isArray(sitesData) ? sitesData : sitesData.data ?? [];
32
32
  const site = sites.find((s) => s.name === websiteName);
33
33
  if ((site == null ? void 0 : site.id) == null || site.id === "") throw new Error(`Website "${websiteName}" not found at ${apiBase}`);
34
- const pagesRes = await fetch(`${apiBase}/cms/websites/${site.id}/pages?locale=en`);
34
+ let website = site;
35
+ try {
36
+ const wRes = await fetch(`${apiBase}/cms/websites/${site.id}`);
37
+ if (wRes.ok) website = await wRes.json();
38
+ } catch {
39
+ }
40
+ const pagesRes = await fetch(`${apiBase}/cms/websites/${site.id}/pages?locale=${website.default_locale || "en"}`);
35
41
  const pagesData = await pagesRes.json();
36
42
  const pages = Array.isArray(pagesData) ? pagesData : pagesData.data ?? [];
43
+ const paths = await expandRoutes(apiBase, pages);
44
+ let redirects = [];
45
+ try {
46
+ const rRes = await fetch(`${apiBase}/cms/websites/${site.id}/redirects`);
47
+ if (rRes.ok) {
48
+ const rData = await rRes.json();
49
+ const items = Array.isArray(rData) ? rData : rData.data ?? [];
50
+ redirects = items.filter((r) => r.from_path && r.to_path);
51
+ }
52
+ } catch {
53
+ }
54
+ const collections = extractCollectionRefs(pages);
55
+ return { websiteId: site.id, website, paths, redirects, collections };
56
+ }
57
+ async function fetchCmsPrerenderPaths(apiBase, websiteName) {
58
+ const { paths } = await fetchCmsSiteData(apiBase, websiteName);
59
+ return paths;
60
+ }
61
+ async function expandRoutes(apiBase, pages) {
37
62
  const routes = [];
38
63
  for (const page of pages) {
39
64
  const slug = page.slug ?? "/";
65
+ if (page.status && page.status !== "published") continue;
40
66
  if (slug.includes(":") && page.data_bindings) {
41
67
  for (const [, binding] of Object.entries(page.data_bindings)) {
42
68
  const b = binding;
@@ -50,7 +76,10 @@ async function fetchCmsPrerenderPaths(apiBase, websiteName) {
50
76
  for (const item of items) {
51
77
  const bindValue = item[b.bind_by];
52
78
  if (bindValue != null && bindValue !== "") {
53
- const concrete = slug.replace(/:([a-z_]+)/g, (_m, p) => p === b.bind_by ? String(bindValue) : String(item[p] ?? ""));
79
+ const concrete = slug.replace(
80
+ /:([a-z_]+)/g,
81
+ (_m, p) => p === b.bind_by ? String(bindValue) : String(item[p] ?? "")
82
+ );
54
83
  routes.push(concrete);
55
84
  }
56
85
  }
@@ -65,6 +94,170 @@ async function fetchCmsPrerenderPaths(apiBase, websiteName) {
65
94
  }
66
95
  return [...new Set(routes)];
67
96
  }
97
+ function extractCollectionRefs(pages) {
98
+ const seen = /* @__PURE__ */ new Set();
99
+ const refs = [];
100
+ for (const page of pages) {
101
+ if (!page.data_bindings) continue;
102
+ for (const binding of Object.values(page.data_bindings)) {
103
+ const b = binding;
104
+ if (b.adapter === "datastore" && b.collection != null && b.collection !== "" && b.store != null && b.store !== "") {
105
+ const key = `${b.store}/${b.collection}`;
106
+ if (!seen.has(key)) {
107
+ seen.add(key);
108
+ refs.push({ store: b.store, collection: b.collection });
109
+ }
110
+ }
111
+ }
112
+ }
113
+ return refs;
114
+ }
115
+ function buildPageHead(options) {
116
+ const { url, resolvedData, website, stateScript } = options;
117
+ const parts = [];
118
+ const meta = (website == null ? void 0 : website.meta) ?? {};
119
+ const page = resolvedData == null ? void 0 : resolvedData.page;
120
+ const ctx = extractPrimaryContext(resolvedData == null ? void 0 : resolvedData.contexts);
121
+ const title = (page == null ? void 0 : page.meta_title) || ctx.title || ((page == null ? void 0 : page.title) && meta.og_site_name ? `${page.title} · ${meta.og_site_name}` : null) || (page == null ? void 0 : page.title) || meta.default_meta_title;
122
+ if (title) {
123
+ const fullTitle = ctx.title && meta.og_site_name && !ctx.title.includes(meta.og_site_name) ? `${esc(ctx.title)} · ${esc(meta.og_site_name)}` : esc(title);
124
+ parts.push(`<title>${fullTitle}</title>`);
125
+ }
126
+ const description = (page == null ? void 0 : page.meta_description) || ctx.description || meta.default_meta_description;
127
+ if (description) {
128
+ parts.push(`<meta name="description" content="${esc(description)}">`);
129
+ }
130
+ const canonicalBase = (meta.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
131
+ if (canonicalBase) {
132
+ parts.push(`<link rel="canonical" href="${esc(canonicalBase + url)}">`);
133
+ }
134
+ const ogTitle = (page == null ? void 0 : page.meta_title) || ctx.title || (page == null ? void 0 : page.title) || meta.og_site_name || meta.default_meta_title;
135
+ const ogDesc = (page == null ? void 0 : page.meta_description) || ctx.description || meta.og_description || meta.default_meta_description;
136
+ const ogImage = (page == null ? void 0 : page.og_image) || ctx.image || meta.default_og_image;
137
+ const ogType = meta.og_type || "website";
138
+ if (ogTitle) parts.push(`<meta property="og:title" content="${esc(ogTitle)}">`);
139
+ if (ogDesc) parts.push(`<meta property="og:description" content="${esc(ogDesc)}">`);
140
+ if (ogImage) parts.push(`<meta property="og:image" content="${esc(ogImage)}">`);
141
+ parts.push(`<meta property="og:type" content="${esc(ogType)}">`);
142
+ if (canonicalBase) parts.push(`<meta property="og:url" content="${esc(canonicalBase + url)}">`);
143
+ if (meta.og_site_name) parts.push(`<meta property="og:site_name" content="${esc(meta.og_site_name)}">`);
144
+ const twitterCard = meta.twitter_card || (ogImage ? "summary_large_image" : "summary");
145
+ parts.push(`<meta name="twitter:card" content="${esc(twitterCard)}">`);
146
+ if (ogTitle) parts.push(`<meta name="twitter:title" content="${esc(ogTitle)}">`);
147
+ if (ogDesc) parts.push(`<meta name="twitter:description" content="${esc(ogDesc)}">`);
148
+ if (ogImage) parts.push(`<meta name="twitter:image" content="${esc(ogImage)}">`);
149
+ if (meta.twitter_site) parts.push(`<meta name="twitter:site" content="${esc(meta.twitter_site)}">`);
150
+ const alternates = resolvedData == null ? void 0 : resolvedData.alternates;
151
+ if (alternates && canonicalBase) {
152
+ for (const [locale, slug] of Object.entries(alternates)) {
153
+ parts.push(`<link rel="alternate" hreflang="${esc(locale)}" href="${esc(canonicalBase + slug)}">`);
154
+ }
155
+ const defaultSlug = alternates[(website == null ? void 0 : website.default_locale) ?? "en"] ?? url;
156
+ parts.push(`<link rel="alternate" hreflang="x-default" href="${esc(canonicalBase + defaultSlug)}">`);
157
+ }
158
+ if (meta.noindex) {
159
+ parts.push('<meta name="robots" content="noindex, nofollow">');
160
+ }
161
+ if (stateScript) {
162
+ parts.push(stateScript);
163
+ }
164
+ return parts.join("\n");
165
+ }
166
+ function buildSiteHead(website) {
167
+ if (!website) return "";
168
+ const parts = [];
169
+ const meta = website.meta ?? {};
170
+ if (website.favicon_url) {
171
+ parts.push(`<link rel="icon" href="${esc(website.favicon_url)}">`);
172
+ }
173
+ if (meta.webclip) {
174
+ parts.push(`<link rel="apple-touch-icon" sizes="180x180" href="${esc(meta.webclip)}">`);
175
+ }
176
+ if (meta.verification_google) {
177
+ parts.push(`<meta name="google-site-verification" content="${esc(meta.verification_google)}">`);
178
+ }
179
+ if (meta.verification_bing) {
180
+ parts.push(`<meta name="msvalidate.01" content="${esc(meta.verification_bing)}">`);
181
+ }
182
+ if (meta.structured_data_org) {
183
+ try {
184
+ const json = typeof meta.structured_data_org === "string" ? meta.structured_data_org : JSON.stringify(meta.structured_data_org);
185
+ parts.push(`<script type="application/ld+json">${json}${"<"}/script>`);
186
+ } catch {
187
+ }
188
+ }
189
+ const plausibleId = website.plausible_site_id;
190
+ if (plausibleId) {
191
+ parts.push(`<script defer data-domain="${esc(plausibleId)}" src="https://plausible.io/js/script.js"><${"/"}script>`);
192
+ }
193
+ return parts.join("\n");
194
+ }
195
+ function generateSitemapXml(options) {
196
+ const { renderedPaths, canonicalBase, defaultChangefreq = "weekly", defaultPriority = "0.7" } = options;
197
+ const base = canonicalBase.replace(/\/$/, "");
198
+ const urls = renderedPaths.map((p) => {
199
+ const priority = p === "/" ? "1.0" : defaultPriority;
200
+ const changefreq = p === "/" ? "daily" : defaultChangefreq;
201
+ return ` <url>
202
+ <loc>${esc(base + p)}</loc>
203
+ <changefreq>${changefreq}</changefreq>
204
+ <priority>${priority}</priority>
205
+ </url>`;
206
+ });
207
+ return `<?xml version="1.0" encoding="UTF-8"?>
208
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
209
+ ${urls.join("\n")}
210
+ </urlset>
211
+ `;
212
+ }
213
+ function generateRobotsTxt(options) {
214
+ const { robotsTxt, canonicalBase, noindex } = options;
215
+ if (noindex) {
216
+ return `User-agent: *
217
+ Disallow: /
218
+ `;
219
+ }
220
+ const base = (canonicalBase || "").replace(/\/$/, "");
221
+ const sitemapLine = base ? `
222
+ Sitemap: ${base}/sitemap.xml
223
+ ` : "";
224
+ if (robotsTxt) {
225
+ if (sitemapLine && !robotsTxt.toLowerCase().includes("sitemap:")) {
226
+ return `${robotsTxt.trimEnd()}
227
+ ${sitemapLine}`;
228
+ }
229
+ return robotsTxt;
230
+ }
231
+ return `User-agent: *
232
+ Allow: /
233
+ ${sitemapLine}`;
234
+ }
235
+ function generateNetlifyRedirects(redirects) {
236
+ if (redirects.length === 0) return "";
237
+ return `${redirects.map((r) => `${r.from_path} ${r.to_path} ${r.status_code}`).join("\n")}
238
+ `;
239
+ }
240
+ function stripTemplateSeoTags(template) {
241
+ return template.replace(/<title>[^<]*<\/title>/gi, "<!--ssg:title-->").replace(/<meta\s[^>]*name\s*=\s*["']description["'][^>]*>/gi, "").replace(/<meta\s[^>]*property\s*=\s*["']og:[^"']*["'][^>]*>/gi, "").replace(/<meta\s[^>]*(?:property|name)\s*=\s*["']twitter:[^"']*["'][^>]*>/gi, "").replace(/<link\s[^>]*rel\s*=\s*["']apple-touch-icon["'][^>]*>/gi, "").replace(/<link\s[^>]*rel\s*=\s*["']icon["'][^>]*>/gi, "").replace(/\n\s*\n\s*\n/g, "\n\n");
242
+ }
243
+ function extractPrimaryContext(contexts) {
244
+ const empty = { title: null, description: null, image: null };
245
+ if (!contexts || typeof contexts !== "object") return empty;
246
+ const ctx = Object.values(contexts).find((c) => c != null);
247
+ if (!ctx) return empty;
248
+ const str = (key) => {
249
+ const v = ctx[key];
250
+ return typeof v === "string" && v.trim() ? v.trim() : null;
251
+ };
252
+ return {
253
+ title: str("meta_title") || str("title"),
254
+ description: str("meta_description") || str("excerpt") || str("description") || str("blurb") || str("summary"),
255
+ image: str("og_image") || str("cover_image_url") || str("image_url") || str("image")
256
+ };
257
+ }
258
+ function esc(s) {
259
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
260
+ }
68
261
  async function polyfillBloxSsgGlobals(options) {
69
262
  const { env } = await import("node:process");
70
263
  const pageUrl = (options == null ? void 0 : options.pageUrl) ?? env.BAGELINK_API_URL ?? "https://example.com/";
@@ -118,13 +311,23 @@ async function prerender({
118
311
  excludePaths = [],
119
312
  failFast = false,
120
313
  maxPages = 5e3,
121
- mode = "dir"
314
+ mode = "dir",
315
+ website = null,
316
+ websiteId = "",
317
+ redirects = [],
318
+ collections = {}
122
319
  } = {}) {
123
320
  const absRoot = path.resolve(root);
124
321
  const absClient = path.resolve(absRoot, clientOutDir);
125
322
  const absServerEntry = path.resolve(absRoot, serverEntry);
126
323
  const templatePath = path.join(absClient, "index.html");
127
- const template = await fs.readFile(templatePath, "utf8");
324
+ const rawTemplate = await fs.readFile(templatePath, "utf8");
325
+ let template = stripTemplateSeoTags(rawTemplate);
326
+ const siteHead = buildSiteHead(website);
327
+ if (siteHead) {
328
+ template = template.replace("</head>", `${siteHead}
329
+ </head>`);
330
+ }
128
331
  const serverMod = await import(node_url.pathToFileURL(absServerEntry).href);
129
332
  if (typeof serverMod.render !== "function") {
130
333
  throw new TypeError(
@@ -153,7 +356,10 @@ async function prerender({
153
356
  try {
154
357
  const { html, head = "", htmlAttrs = "" } = await serverMod.render(urlPath, {
155
358
  manifest,
156
- template
359
+ template,
360
+ website,
361
+ websiteId,
362
+ collections
157
363
  });
158
364
  const outHtml = injectIntoTemplate(template, head, html, fontPreloads, htmlAttrs);
159
365
  const outfile = outFilePath(absClient, urlPath, mode);
@@ -171,6 +377,31 @@ async function prerender({
171
377
  `, err.stack ?? err);
172
378
  }
173
379
  }
380
+ const websiteMeta = (website == null ? void 0 : website.meta) ?? {};
381
+ const canonicalBase = (websiteMeta.canonical_base_url || (website == null ? void 0 : website.domain) || "").replace(/\/$/, "");
382
+ if (canonicalBase && rendered.length > 0) {
383
+ const sitemap = generateSitemapXml({ renderedPaths: rendered, canonicalBase });
384
+ await fs.writeFile(path.join(absClient, "sitemap.xml"), sitemap, "utf8");
385
+ console.log(` Generated sitemap.xml (${rendered.length} URLs)`);
386
+ }
387
+ const robotsTxt = generateRobotsTxt({
388
+ robotsTxt: website == null ? void 0 : website.robots_txt,
389
+ canonicalBase,
390
+ noindex: websiteMeta.noindex
391
+ });
392
+ await fs.writeFile(path.join(absClient, "robots.txt"), robotsTxt, "utf8");
393
+ console.log(" Generated robots.txt");
394
+ if (redirects.length > 0) {
395
+ const redirectsContent = generateNetlifyRedirects(redirects);
396
+ const redirectsPath = path.join(absClient, "_redirects");
397
+ let existing = "";
398
+ try {
399
+ existing = await fs.readFile(redirectsPath, "utf8");
400
+ } catch {
401
+ }
402
+ await fs.writeFile(redirectsPath, redirectsContent + existing, "utf8");
403
+ console.log(` Generated _redirects (${redirects.length} rules)`);
404
+ }
174
405
  return {
175
406
  rendered,
176
407
  failures,
@@ -256,6 +487,9 @@ function injectIntoTemplate(template, head, appHtml, fontPreloads, htmlAttrs = "
256
487
  </head>`);
257
488
  }
258
489
  if (head) {
490
+ if (/<title>/.test(head)) {
491
+ out = out.replace("<!--ssg:title-->", "");
492
+ }
259
493
  const descTagRe = /<meta\s[^>]*name\s*=\s*["']description["'][^>]*>/gi;
260
494
  const descMatches = [...head.matchAll(descTagRe)];
261
495
  if (descMatches.length > 1) {
@@ -298,6 +532,12 @@ function discoverInternalLinks(html) {
298
532
  }
299
533
  return [...out];
300
534
  }
535
+ exports.buildPageHead = buildPageHead;
536
+ exports.buildSiteHead = buildSiteHead;
301
537
  exports.fetchCmsPrerenderPaths = fetchCmsPrerenderPaths;
538
+ exports.fetchCmsSiteData = fetchCmsSiteData;
539
+ exports.generateNetlifyRedirects = generateNetlifyRedirects;
540
+ exports.generateRobotsTxt = generateRobotsTxt;
541
+ exports.generateSitemapXml = generateSitemapXml;
302
542
  exports.polyfillBloxSsgGlobals = polyfillBloxSsgGlobals;
303
543
  exports.prerender = prerender;