@rimelight/seo 0.0.12 → 0.0.14

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.
@@ -274,10 +274,18 @@ function rimelightSeo(options = {}) {
274
274
  if (id === "virtual:rimelight-seo/site-config") return "\0virtual:rimelight-seo/site-config";
275
275
  if (id === "virtual:rimelight-seo/db") return "\0virtual:rimelight-seo/db";
276
276
  if (id === "virtual:rimelight-seo/corpus") return "\0virtual:rimelight-seo/corpus";
277
+ if (id === "virtual:rimelight-seo/assets") return "\0virtual:rimelight-seo/assets";
277
278
  return null;
278
279
  },
279
280
  load(id) {
280
281
  if (id === "\0virtual:rimelight-seo/options") return `export default ${JSON.stringify(options)};`;
282
+ if (id === "\0virtual:rimelight-seo/assets") {
283
+ const b64 = masterIconBuffer ? masterIconBuffer.toString("base64") : null;
284
+ return `const b64 = ${JSON.stringify(b64)};
285
+ export const masterIconBuffer = b64 ? (typeof Buffer !== "undefined" ? Buffer.from(b64, "base64") : Uint8Array.from(atob(b64), (c) => c.charCodeAt(0))) : null;
286
+ export const isSvg = ${JSON.stringify(isSvg)};
287
+ export const backgroundColor = ${JSON.stringify(bgColor)};`;
288
+ }
281
289
  if (id === "\0virtual:rimelight/seo") return `export const siteConfig = ${JSON.stringify(normalizedSiteConfig)};
282
290
  export const SEO_LOCALES = ${JSON.stringify(seoLocales)};
283
291
  export const PRIVATE_PATH_PREFIXES = ${JSON.stringify(privatePathPrefixes)};
@@ -12,6 +12,7 @@ const GET = () => {
12
12
  description: manifestOpts.description || siteConfig.description || "",
13
13
  backgroundColor: manifestOpts.backgroundColor || siteConfig.branding?.colors?.backgroundColor || "#ffffff",
14
14
  themeColor: manifestOpts.themeColor || siteConfig.branding?.colors?.themeColor || "#ffffff",
15
+ icons: manifestOpts.icons || siteConfig.branding?.icons,
15
16
  ...manifestOpts
16
17
  });
17
18
  return new Response(JSON.stringify(manifest, null, 2), { headers: {
package/dist/types.d.mts CHANGED
@@ -121,15 +121,23 @@ export interface SiteConfig {
121
121
  branding: {
122
122
  logo: {
123
123
  alt: string;
124
+ src?: string;
125
+ [key: string]: unknown;
124
126
  };
125
127
  favicon?: {
126
- svg: string;
127
- };
128
+ svg?: string;
129
+ ico?: string;
130
+ png?: string;
131
+ [key: string]: string | undefined;
132
+ } | string;
133
+ appleTouchIcon?: string;
134
+ icons?: WebManifestIcon[];
128
135
  icon?: string;
129
136
  colors: {
130
137
  themeColor: string;
131
138
  backgroundColor: string;
132
139
  };
140
+ [key: string]: unknown;
133
141
  };
134
142
  seo: {
135
143
  titleTemplate: string;
@@ -215,9 +223,13 @@ export interface HeadProps {
215
223
  href: string;
216
224
  }>;
217
225
  /**
218
- * Custom favicon SVG or image path (overrides config.branding.favicon.svg)
226
+ * Custom favicon SVG or image path (overrides config.branding.favicon)
219
227
  */
220
228
  favicon?: string;
229
+ /**
230
+ * Custom Apple Touch Icon path or CDN URL (overrides config.branding.appleTouchIcon)
231
+ */
232
+ appleTouchIcon?: string;
221
233
  /**
222
234
  * Site configuration for defaults
223
235
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rimelight/seo",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "private": false,
5
5
  "description": "Rimelight Entertainment's SEO Package",
6
6
  "homepage": "https://rimelight.com/docs",
@@ -73,7 +73,7 @@
73
73
  "access": "public"
74
74
  },
75
75
  "devDependencies": {
76
- "@rimelight/config": "0.0.13",
76
+ "@rimelight/config": "0.0.17",
77
77
  "astro": "7.3.3",
78
78
  "sharp": "^0.35.4",
79
79
  "typescript": "6.0.3"
@@ -23,6 +23,7 @@ const {
23
23
  languageAlternates = [],
24
24
  config,
25
25
  favicon: customFavicon,
26
+ appleTouchIcon: customAppleTouchIcon,
26
27
  charset = 'utf-8',
27
28
  viewport = 'width=device-width, initial-scale=1',
28
29
  rssFeed,
@@ -41,12 +42,17 @@ const siteName = config?.name || 'Rimelight'
41
42
  const siteDescription = config?.description || ''
42
43
  const ogImageFallback = config?.seo?.ogImageFallback || config?.ogImage || '/og.png'
43
44
  const themeColor = config?.branding?.colors?.themeColor
44
- const favicon = customFavicon || config?.branding?.favicon?.svg || '/favicon.svg'
45
+
46
+ const rawFavicon = customFavicon || (typeof config?.branding?.favicon === 'string' ? config.branding.favicon : config?.branding?.favicon?.svg) || config?.branding?.icon || '/favicon.svg'
47
+ const rawFaviconIco = typeof config?.branding?.favicon === 'object' ? config?.branding?.favicon?.ico : undefined
48
+ const favicon = rawFavicon
45
49
  const faviconType = favicon.endsWith('.ico')
46
50
  ? 'image/x-icon'
47
51
  : favicon.endsWith('.png')
48
52
  ? 'image/png'
49
53
  : 'image/svg+xml'
54
+
55
+ const appleTouchIcon = customAppleTouchIcon || config?.branding?.appleTouchIcon || '/apple-touch-icon.png'
50
56
  const twitterHandle = config?.seo?.authorHandle
51
57
  const titleTemplate = config?.seo?.titleTemplate || `%s | ${siteName}`
52
58
 
@@ -171,8 +177,8 @@ if (!resolvedTwitter.card) {
171
177
 
172
178
  <!-- Icons & Manifest -->
173
179
  <link rel="icon" type={faviconType} href={favicon} />
174
- {!customFavicon && <link rel="icon" href="/favicon.ico" sizes="32x32" />}
175
- <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
180
+ {rawFaviconIco ? <link rel="icon" href={rawFaviconIco} sizes="32x32" /> : (!customFavicon && <link rel="icon" href="/favicon.ico" sizes="32x32" />)}
181
+ <link rel="apple-touch-icon" href={appleTouchIcon} />
176
182
  <link rel="manifest" href="/site.webmanifest" />
177
183
 
178
184
  <!-- Theme Metadata -->
@@ -358,12 +358,22 @@ export function rimelightSeo(options: RimelightSeoOptions = {}): AstroIntegratio
358
358
  if (id === "virtual:rimelight-seo/corpus") {
359
359
  return "\0virtual:rimelight-seo/corpus"
360
360
  }
361
+ if (id === "virtual:rimelight-seo/assets") {
362
+ return "\0virtual:rimelight-seo/assets"
363
+ }
361
364
  return null
362
365
  },
363
366
  load(id) {
364
367
  if (id === "\0virtual:rimelight-seo/options") {
365
368
  return `export default ${JSON.stringify(options)};`
366
369
  }
370
+ if (id === "\0virtual:rimelight-seo/assets") {
371
+ const b64 = masterIconBuffer ? masterIconBuffer.toString("base64") : null
372
+ return `const b64 = ${JSON.stringify(b64)};
373
+ export const masterIconBuffer = b64 ? (typeof Buffer !== "undefined" ? Buffer.from(b64, "base64") : Uint8Array.from(atob(b64), (c) => c.charCodeAt(0))) : null;
374
+ export const isSvg = ${JSON.stringify(isSvg)};
375
+ export const backgroundColor = ${JSON.stringify(bgColor)};`
376
+ }
367
377
  if (id === "\0virtual:rimelight/seo") {
368
378
  return `export const siteConfig = ${JSON.stringify(normalizedSiteConfig)};
369
379
  export const SEO_LOCALES = ${JSON.stringify(seoLocales)};
@@ -38,4 +38,28 @@ describe("buildWebManifest", () => {
38
38
  expect(manifest.background_color).toBe("#111111")
39
39
  expect(manifest.shortcuts).toHaveLength(1)
40
40
  })
41
+
42
+ it("supports remote CDN icon URLs", () => {
43
+ const manifest = buildWebManifest({
44
+ name: "CDN App",
45
+ icons: [
46
+ {
47
+ src: "https://cdn.example.com/icons/icon-192.png",
48
+ sizes: "192x192",
49
+ type: "image/png",
50
+ purpose: "any"
51
+ },
52
+ {
53
+ src: "https://cdn.example.com/icons/icon-512.png",
54
+ sizes: "512x512",
55
+ type: "image/png",
56
+ purpose: "maskable"
57
+ }
58
+ ]
59
+ })
60
+
61
+ expect(manifest.icons).toHaveLength(2)
62
+ expect(manifest.icons?.[0]?.src).toBe("https://cdn.example.com/icons/icon-192.png")
63
+ expect(manifest.icons?.[1]?.src).toBe("https://cdn.example.com/icons/icon-512.png")
64
+ })
41
65
  })
package/src/og.test.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { describe, it, expect } from "vitest"
1
+ import { describe, it, expect } from "vite-plus/test"
2
2
  import { defaultOgLayout, NOTO_SANS } from "./og"
3
3
 
4
4
  describe("@rimelight/seo defaultOgLayout", () => {
@@ -16,6 +16,7 @@ export const GET: APIRoute = () => {
16
16
  backgroundColor:
17
17
  manifestOpts.backgroundColor || siteConfig.branding?.colors?.backgroundColor || "#ffffff",
18
18
  themeColor: manifestOpts.themeColor || siteConfig.branding?.colors?.themeColor || "#ffffff",
19
+ icons: manifestOpts.icons || siteConfig.branding?.icons,
19
20
  ...manifestOpts
20
21
  })
21
22
 
package/src/types.ts CHANGED
@@ -129,15 +129,25 @@ export interface SiteConfig {
129
129
  branding: {
130
130
  logo: {
131
131
  alt: string
132
+ src?: string
133
+ [key: string]: unknown
132
134
  }
133
- favicon?: {
134
- svg: string
135
- }
135
+ favicon?:
136
+ | {
137
+ svg?: string
138
+ ico?: string
139
+ png?: string
140
+ [key: string]: string | undefined
141
+ }
142
+ | string
143
+ appleTouchIcon?: string
144
+ icons?: WebManifestIcon[]
136
145
  icon?: string
137
146
  colors: {
138
147
  themeColor: string
139
148
  backgroundColor: string
140
149
  }
150
+ [key: string]: unknown
141
151
  }
142
152
  seo: {
143
153
  titleTemplate: string
@@ -223,9 +233,13 @@ export interface HeadProps {
223
233
  */
224
234
  languageAlternates?: Array<{ hreflang: string; href: string }>
225
235
  /**
226
- * Custom favicon SVG or image path (overrides config.branding.favicon.svg)
236
+ * Custom favicon SVG or image path (overrides config.branding.favicon)
227
237
  */
228
238
  favicon?: string
239
+ /**
240
+ * Custom Apple Touch Icon path or CDN URL (overrides config.branding.appleTouchIcon)
241
+ */
242
+ appleTouchIcon?: string
229
243
  /**
230
244
  * Site configuration for defaults
231
245
  */