@rimelight/seo 0.0.5 → 0.0.7

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 (74) hide show
  1. package/dist/assets.d.mts +39 -0
  2. package/dist/assets.mjs +96 -0
  3. package/dist/catalog.d.mts +7 -0
  4. package/dist/catalog.mjs +10 -0
  5. package/dist/index.d.mts +9 -3
  6. package/dist/index.mjs +10 -2
  7. package/dist/integration.d.mts +3 -20
  8. package/dist/integration.mjs +257 -7
  9. package/dist/manifest.d.mts +7 -0
  10. package/dist/manifest.mjs +44 -0
  11. package/dist/og.d.mts +117 -0
  12. package/dist/og.mjs +302 -0
  13. package/dist/routes/api-catalog.d.mts +5 -0
  14. package/dist/routes/api-catalog.mjs +38 -0
  15. package/dist/routes/apple-touch-icon.png.d.mts +5 -0
  16. package/dist/routes/apple-touch-icon.png.mjs +17 -0
  17. package/dist/routes/favicon.ico.d.mts +5 -0
  18. package/dist/routes/favicon.ico.mjs +14 -0
  19. package/dist/routes/favicon.svg.d.mts +5 -0
  20. package/dist/routes/favicon.svg.mjs +12 -0
  21. package/dist/routes/icon-192.png.d.mts +5 -0
  22. package/dist/routes/icon-192.png.mjs +17 -0
  23. package/dist/routes/icon-512.png.d.mts +5 -0
  24. package/dist/routes/icon-512.png.mjs +17 -0
  25. package/dist/routes/icon-maskable-512.png.d.mts +5 -0
  26. package/dist/routes/icon-maskable-512.png.mjs +18 -0
  27. package/dist/routes/llms-full.txt.d.mts +5 -0
  28. package/dist/routes/llms-full.txt.mjs +57 -0
  29. package/dist/routes/llms.txt.d.mts +1 -0
  30. package/dist/routes/llms.txt.mjs +60 -12
  31. package/dist/routes/robots.txt.mjs +32 -4
  32. package/dist/routes/rss.xml.d.mts +4 -0
  33. package/dist/routes/rss.xml.mjs +79 -0
  34. package/dist/routes/security.txt.d.mts +5 -0
  35. package/dist/routes/security.txt.mjs +24 -0
  36. package/dist/routes/site.webmanifest.d.mts +5 -0
  37. package/dist/routes/site.webmanifest.mjs +23 -0
  38. package/dist/routes/sitemap.xml.mjs +18 -4
  39. package/dist/rss.d.mts +8 -0
  40. package/dist/rss.mjs +44 -0
  41. package/dist/security.d.mts +7 -0
  42. package/dist/security.mjs +34 -0
  43. package/dist/types.d.mts +263 -3
  44. package/package.json +38 -3
  45. package/src/assets.test.ts +70 -0
  46. package/src/assets.ts +137 -0
  47. package/src/catalog.test.ts +26 -0
  48. package/src/catalog.ts +11 -0
  49. package/src/components/SEOHead.astro +1 -1
  50. package/src/env.d.ts +51 -3
  51. package/src/index.ts +33 -2
  52. package/src/integration.ts +307 -28
  53. package/src/manifest.test.ts +41 -0
  54. package/src/manifest.ts +59 -0
  55. package/src/og.ts +436 -0
  56. package/src/routes/api-catalog.ts +55 -0
  57. package/src/routes/apple-touch-icon.png.ts +19 -0
  58. package/src/routes/favicon.ico.ts +19 -0
  59. package/src/routes/favicon.svg.ts +17 -0
  60. package/src/routes/icon-192.png.ts +19 -0
  61. package/src/routes/icon-512.png.ts +19 -0
  62. package/src/routes/icon-maskable-512.png.ts +24 -0
  63. package/src/routes/llms-full.txt.ts +77 -0
  64. package/src/routes/llms.txt.ts +84 -13
  65. package/src/routes/robots.txt.ts +47 -4
  66. package/src/routes/rss.xml.ts +129 -0
  67. package/src/routes/security.txt.ts +30 -0
  68. package/src/routes/site.webmanifest.ts +28 -0
  69. package/src/routes/sitemap.xml.ts +32 -5
  70. package/src/rss.test.ts +28 -0
  71. package/src/rss.ts +63 -0
  72. package/src/security.test.ts +46 -0
  73. package/src/security.ts +78 -0
  74. package/src/types.ts +286 -3
@@ -0,0 +1,39 @@
1
+ //#region src/assets.d.ts
2
+ export interface GeneratePngOptions {
3
+ width: number;
4
+ height: number;
5
+ background?: string;
6
+ }
7
+ export interface GenerateMaskableOptions {
8
+ size?: number;
9
+ background?: string;
10
+ paddingRatio?: number;
11
+ }
12
+ export interface GenerateOgCardOptions {
13
+ width?: number;
14
+ height?: number;
15
+ background?: string;
16
+ logoBuffer?: Buffer | Uint8Array | string | null;
17
+ }
18
+ /**
19
+ * Generate a PNG buffer resized to specific dimensions from an SVG or image buffer.
20
+ */
21
+ export declare function generatePng(source: Buffer | Uint8Array | string, options: {
22
+ width: number;
23
+ height: number;
24
+ }): Promise<Buffer>;
25
+ /**
26
+ * Generate a maskable PWA PNG icon with safe-zone padding and solid background. The standard
27
+ * recommendation is to place the master icon in the inner 80% safe zone.
28
+ */
29
+ export declare function generateMaskablePng(source: Buffer | Uint8Array | string, options?: GenerateMaskableOptions): Promise<Buffer>;
30
+ /**
31
+ * Generate a valid .ico buffer containing a 32x32 PNG image according to the Windows ICO
32
+ * specification.
33
+ */
34
+ export declare function generateIco(source: Buffer | Uint8Array | string, size?: number): Promise<Buffer>;
35
+ /**
36
+ * Generate a standard 1200x630 branded Open Graph card from a master logo and background color.
37
+ */
38
+ export declare function generateOgCard(options?: GenerateOgCardOptions): Promise<Buffer>;
39
+ //#endregion
@@ -0,0 +1,96 @@
1
+ import sharp from "sharp";
2
+ //#region src/assets.ts
3
+ /**
4
+ * Generate a PNG buffer resized to specific dimensions from an SVG or image buffer.
5
+ */
6
+ async function generatePng(source, options) {
7
+ return sharp(source).resize(options.width, options.height, {
8
+ fit: "contain",
9
+ background: {
10
+ r: 0,
11
+ g: 0,
12
+ b: 0,
13
+ alpha: 0
14
+ }
15
+ }).png().toBuffer();
16
+ }
17
+ /**
18
+ * Generate a maskable PWA PNG icon with safe-zone padding and solid background. The standard
19
+ * recommendation is to place the master icon in the inner 80% safe zone.
20
+ */
21
+ async function generateMaskablePng(source, options = {}) {
22
+ const { size = 512, background = "#ffffff", paddingRatio = .15 } = options;
23
+ const innerSize = Math.round(size * (1 - paddingRatio * 2));
24
+ const pad = Math.floor((size - innerSize) / 2);
25
+ const innerPng = await sharp(source).resize(innerSize, innerSize, {
26
+ fit: "contain",
27
+ background: {
28
+ r: 0,
29
+ g: 0,
30
+ b: 0,
31
+ alpha: 0
32
+ }
33
+ }).png().toBuffer();
34
+ return sharp(innerPng).extend({
35
+ top: pad,
36
+ bottom: size - innerSize - pad,
37
+ left: pad,
38
+ right: size - innerSize - pad,
39
+ background
40
+ }).png().toBuffer();
41
+ }
42
+ /**
43
+ * Generate a valid .ico buffer containing a 32x32 PNG image according to the Windows ICO
44
+ * specification.
45
+ */
46
+ async function generateIco(source, size = 32) {
47
+ const pngBuffer = await generatePng(source, {
48
+ width: size,
49
+ height: size
50
+ });
51
+ const header = Buffer.alloc(22);
52
+ header.writeUInt16LE(0, 0);
53
+ header.writeUInt16LE(1, 2);
54
+ header.writeUInt16LE(1, 4);
55
+ header.writeUInt8(size === 256 ? 0 : size, 6);
56
+ header.writeUInt8(size === 256 ? 0 : size, 7);
57
+ header.writeUInt8(0, 8);
58
+ header.writeUInt8(0, 9);
59
+ header.writeUInt16LE(1, 10);
60
+ header.writeUInt16LE(32, 12);
61
+ header.writeUInt32LE(pngBuffer.length, 14);
62
+ header.writeUInt32LE(22, 18);
63
+ return Buffer.concat([header, pngBuffer]);
64
+ }
65
+ /**
66
+ * Generate a standard 1200x630 branded Open Graph card from a master logo and background color.
67
+ */
68
+ async function generateOgCard(options = {}) {
69
+ const { width = 1200, height = 630, background = "#0a0a0a", logoBuffer } = options;
70
+ if (logoBuffer) {
71
+ const logoSize = Math.min(Math.round(height * .45), 280);
72
+ const logoPng = await generatePng(logoBuffer, {
73
+ width: logoSize,
74
+ height: logoSize
75
+ });
76
+ const topPad = Math.floor((height - logoSize) / 2);
77
+ const bottomPad = height - logoSize - topPad;
78
+ const leftPad = Math.floor((width - logoSize) / 2);
79
+ const rightPad = width - logoSize - leftPad;
80
+ return sharp(logoPng).extend({
81
+ top: topPad,
82
+ bottom: bottomPad,
83
+ left: leftPad,
84
+ right: rightPad,
85
+ background
86
+ }).png().toBuffer();
87
+ }
88
+ return sharp({ create: {
89
+ width,
90
+ height,
91
+ channels: 4,
92
+ background
93
+ } }).png().toBuffer();
94
+ }
95
+ //#endregion
96
+ export { generateIco, generateMaskablePng, generateOgCard, generatePng };
@@ -0,0 +1,7 @@
1
+ import { ApiCatalog, ApiCatalogOptions } from "./types.mjs";
2
+ //#region src/catalog.d.ts
3
+ /**
4
+ * Build RFC 9652 Application Linkset API Catalog. https://www.rfc-editor.org/rfc/rfc9652
5
+ */
6
+ export declare function buildApiCatalog(options: ApiCatalogOptions): ApiCatalog;
7
+ //#endregion
@@ -0,0 +1,10 @@
1
+ //#region src/catalog.ts
2
+ /**
3
+ * Build RFC 9652 Application Linkset API Catalog. https://www.rfc-editor.org/rfc/rfc9652
4
+ */
5
+ function buildApiCatalog(options) {
6
+ const { items } = options;
7
+ return { "api-catalog": items };
8
+ }
9
+ //#endregion
10
+ export { buildApiCatalog };
package/dist/index.d.mts CHANGED
@@ -1,8 +1,14 @@
1
+ import { generateIco, generateMaskablePng, generateOgCard, generatePng } from "./assets.mjs";
1
2
  import { i as buildBreadcrumbSchema, n as BreadcrumbItem, r as buildArticleSchema, t as ArticleSchemaOptions } from "./schema-C8v69blQ.mjs";
2
3
  import { a as buildLlmsTxt, i as buildLlmsFullTxt, n as LlmsPage, r as LlmsTxtOptions, t as LlmsFullTxtOptions } from "./llms-BVLxzjr-.mjs";
3
- import { ChangeFreq, HeadProps, LocaleConfig, OGImage, OpenGraphMeta, RSSFeed, RobotsOptions, SeoEntry, SiteConfig, SitemapAlternate, SitemapUrl, TwitterMeta, UserAgentRule } from "./types.mjs";
4
+ import { ApiCatalog, ApiCatalogItem, ApiCatalogOptions, ApiCatalogRouteOptions, AssetRouteOptions, ChangeFreq, HeadProps, LlmsFullRouteOptions, LlmsRouteOptions, LocaleConfig, ManifestRouteOptions, OGImage, OpenGraphMeta, RSSFeed, RimelightSeoOptions, RobotsOptions, RobotsRouteOptions, RssCollectionConfig, RssFeedOptions, RssItem, RssOptions, SecurityRouteOptions, SecurityTxtOptions, SeoEntry, SiteConfig, SitemapAlternate, SitemapOptions, SitemapUrl, TwitterMeta, UserAgentRule, WebManifest, WebManifestIcon, WebManifestOptions, WebManifestShortcut } from "./types.mjs";
5
+ import { buildApiCatalog } from "./catalog.mjs";
4
6
  import { buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, chunkSitemapUrls } from "./sitemap.mjs";
5
7
  import { buildRobotsTxt } from "./robots.mjs";
8
+ import { buildRssXml, escapeXml } from "./rss.mjs";
6
9
  import { buildCanonicalUrl, buildPageTitle, buildRobotsMetaContent, buildSafeDescription, buildWebSiteSchema, deepMerge } from "./meta.mjs";
7
- import { RimelightSeoOptions, rimelightSeo } from "./integration.mjs";
8
- export { type ArticleSchemaOptions, type BreadcrumbItem, type ChangeFreq, type HeadProps, type LlmsFullTxtOptions, type LlmsPage, type LlmsTxtOptions, type LocaleConfig, type OGImage, type OpenGraphMeta, type RSSFeed, type RimelightSeoOptions, type RobotsOptions, type SeoEntry, type SiteConfig, type SitemapAlternate, type SitemapUrl, type TwitterMeta, type UserAgentRule, buildArticleSchema, buildBreadcrumbSchema, buildCanonicalUrl, buildLlmsFullTxt, buildLlmsTxt, buildPageTitle, buildRobotsMetaContent, buildRobotsTxt, buildSafeDescription, buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, buildWebSiteSchema, chunkSitemapUrls, deepMerge, rimelightSeo };
10
+ import { buildSecurityTxt } from "./security.mjs";
11
+ import { buildWebManifest } from "./manifest.mjs";
12
+ import { OgFontConfig, OgLayoutOptions, OgRenderConfig } from "./og.mjs";
13
+ import rimelightSeo from "./integration.mjs";
14
+ export { type ApiCatalog, type ApiCatalogItem, type ApiCatalogOptions, type ApiCatalogRouteOptions, type ArticleSchemaOptions, type AssetRouteOptions, type BreadcrumbItem, type ChangeFreq, type HeadProps, type LlmsFullRouteOptions, type LlmsFullTxtOptions, type LlmsPage, type LlmsRouteOptions, type LlmsTxtOptions, type LocaleConfig, type ManifestRouteOptions, type OGImage, type OgFontConfig, type OgLayoutOptions, type OgRenderConfig, type OpenGraphMeta, type RSSFeed, type RimelightSeoOptions, type RobotsOptions, type RobotsRouteOptions, type RssCollectionConfig, type RssFeedOptions, type RssItem, type RssOptions, type SecurityRouteOptions, type SecurityTxtOptions, type SeoEntry, type SiteConfig, type SitemapAlternate, type SitemapOptions, type SitemapUrl, type TwitterMeta, type UserAgentRule, type WebManifest, type WebManifestIcon, type WebManifestOptions, type WebManifestShortcut, buildApiCatalog, buildArticleSchema, buildBreadcrumbSchema, buildCanonicalUrl, buildLlmsFullTxt, buildLlmsTxt, buildPageTitle, buildRobotsMetaContent, buildRobotsTxt, buildRssXml, buildSafeDescription, buildSecurityTxt, buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, buildWebManifest, buildWebSiteSchema, chunkSitemapUrls, deepMerge, rimelightSeo as default, rimelightSeo, escapeXml, generateIco, generateMaskablePng, generateOgCard, generatePng };
package/dist/index.mjs CHANGED
@@ -1,7 +1,15 @@
1
+ import { generateIco, generateMaskablePng, generateOgCard, generatePng } from "./assets.mjs";
2
+ import { buildApiCatalog } from "./catalog.mjs";
1
3
  import { buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, chunkSitemapUrls } from "./sitemap.mjs";
2
4
  import { buildRobotsTxt } from "./robots.mjs";
5
+ import { buildRssXml, escapeXml } from "./rss.mjs";
3
6
  import { buildCanonicalUrl, buildPageTitle, buildRobotsMetaContent, buildSafeDescription, buildWebSiteSchema, deepMerge } from "./meta.mjs";
4
7
  import { buildLlmsFullTxt, buildLlmsTxt } from "./llms.mjs";
8
+ import { buildSecurityTxt } from "./security.mjs";
9
+ import { buildWebManifest } from "./manifest.mjs";
5
10
  import { buildArticleSchema, buildBreadcrumbSchema } from "./schema.mjs";
6
- import { rimelightSeo } from "./integration.mjs";
7
- export { buildArticleSchema, buildBreadcrumbSchema, buildCanonicalUrl, buildLlmsFullTxt, buildLlmsTxt, buildPageTitle, buildRobotsMetaContent, buildRobotsTxt, buildSafeDescription, buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, buildWebSiteSchema, chunkSitemapUrls, deepMerge, rimelightSeo };
11
+ import rimelightSeo from "./integration.mjs";
12
+ //#region src/index.ts
13
+ var src_default = rimelightSeo;
14
+ //#endregion
15
+ export { buildApiCatalog, buildArticleSchema, buildBreadcrumbSchema, buildCanonicalUrl, buildLlmsFullTxt, buildLlmsTxt, buildPageTitle, buildRobotsMetaContent, buildRobotsTxt, buildRssXml, buildSafeDescription, buildSecurityTxt, buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, buildWebManifest, buildWebSiteSchema, chunkSitemapUrls, deepMerge, src_default as default, escapeXml, generateIco, generateMaskablePng, generateOgCard, generatePng, rimelightSeo };
@@ -1,23 +1,6 @@
1
- import { r as LlmsTxtOptions } from "./llms-BVLxzjr-.mjs";
2
- import { RobotsOptions, SitemapUrl } from "./types.mjs";
1
+ import { RimelightSeoOptions } from "./types.mjs";
3
2
  import { AstroIntegration } from "astro";
4
3
  //#region src/integration.d.ts
5
- export interface RimelightSeoOptions {
6
- /**
7
- * Sitemap configuration or false to disable automatic /sitemap.xml
8
- */
9
- sitemap?: boolean | {
10
- urls?: SitemapUrl[];
11
- [key: string]: any;
12
- };
13
- /**
14
- * Robots.txt configuration or false to disable automatic /robots.txt
15
- */
16
- robots?: boolean | RobotsOptions;
17
- /**
18
- * LLMs.txt configuration or false to disable automatic /llms.txt
19
- */
20
- llms?: boolean | LlmsTxtOptions;
21
- }
22
4
  export declare function rimelightSeo(options?: RimelightSeoOptions): AstroIntegration;
23
- //#endregion
5
+ //#endregion
6
+ export { type RimelightSeoOptions, rimelightSeo as default };
@@ -1,28 +1,278 @@
1
+ import { generateIco, generateMaskablePng, generateOgCard, generatePng } from "./assets.mjs";
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+ import { fileURLToPath } from "node:url";
1
5
  //#region src/integration.ts
2
6
  function rimelightSeo(options = {}) {
3
7
  return {
4
8
  name: "@rimelight/seo",
5
- hooks: { "astro:config:setup": ({ injectRoute, updateConfig }) => {
9
+ hooks: { "astro:config:setup": ({ injectRoute, updateConfig, config }) => {
10
+ const rootDir = fileURLToPath(config.root);
11
+ const findFile = (candidates) => {
12
+ for (const c of candidates) {
13
+ const resolved = path.resolve(rootDir, c);
14
+ if (fs.existsSync(resolved)) return resolved;
15
+ }
16
+ return null;
17
+ };
18
+ const seoConfigPath = findFile([
19
+ "src/config/seo.config.ts",
20
+ "src/config/seo.config.js",
21
+ "src/config/seo.config.mjs"
22
+ ]);
23
+ const siteConfigPath = findFile([
24
+ "src/config/site.config.ts",
25
+ "src/config/site.config.js",
26
+ "src/config/site.config.mjs"
27
+ ]);
28
+ const dbPath = findFile([
29
+ "src/db/index.ts",
30
+ "src/db/index.js",
31
+ "src/db/index.mjs"
32
+ ]);
33
+ const corpusPath = findFile([
34
+ "src/config/llms.corpus.ts",
35
+ "src/config/llms.corpus.js",
36
+ "src/config/llms.corpus.mjs"
37
+ ]);
38
+ const assetOpts = typeof options.assets === "object" ? options.assets : {};
39
+ const explicitIcon = assetOpts.icon ? path.resolve(rootDir, assetOpts.icon) : null;
40
+ const masterIconPath = explicitIcon && fs.existsSync(explicitIcon) ? explicitIcon : findFile([
41
+ "src/assets/logos/logomark_color.svg",
42
+ "src/assets/logos/logo_color.svg",
43
+ "src/assets/logos/logomark.svg",
44
+ "src/assets/logos/logo.svg",
45
+ "src/assets/icon.svg",
46
+ "src/assets/logo.svg",
47
+ "src/assets/icon.png",
48
+ "public/favicon.svg"
49
+ ]);
50
+ let masterIconBuffer = null;
51
+ let isSvg = false;
52
+ if (masterIconPath && fs.existsSync(masterIconPath)) {
53
+ masterIconBuffer = fs.readFileSync(masterIconPath);
54
+ isSvg = masterIconPath.endsWith(".svg");
55
+ }
56
+ const bgColor = assetOpts.backgroundColor || "#ffffff";
6
57
  if (options.robots !== false) injectRoute({
7
- pattern: "/robots.txt",
58
+ pattern: typeof options.robots === "object" && options.robots.path ? options.robots.path : "/robots.txt",
8
59
  entrypoint: "@rimelight/seo/routes/robots.txt.ts"
9
60
  });
10
61
  if (options.sitemap !== false) injectRoute({
11
- pattern: "/sitemap.xml",
62
+ pattern: typeof options.sitemap === "object" && options.sitemap.path ? options.sitemap.path : "/sitemap.xml",
12
63
  entrypoint: "@rimelight/seo/routes/sitemap.xml.ts"
13
64
  });
65
+ if (options.rss !== false) injectRoute({
66
+ pattern: typeof options.rss === "object" && options.rss.path ? options.rss.path : "/rss.xml",
67
+ entrypoint: "@rimelight/seo/routes/rss.xml.ts"
68
+ });
14
69
  if (options.llms !== false) injectRoute({
15
- pattern: "/llms.txt",
70
+ pattern: typeof options.llms === "object" && options.llms.path ? options.llms.path : "/llms.txt",
16
71
  entrypoint: "@rimelight/seo/routes/llms.txt.ts"
17
72
  });
73
+ if (options.llmsFull !== false) injectRoute({
74
+ pattern: typeof options.llmsFull === "object" && options.llmsFull.path ? options.llmsFull.path : "/llms-full.txt",
75
+ entrypoint: "@rimelight/seo/routes/llms-full.txt.ts"
76
+ });
77
+ if (options.security !== false) {
78
+ const pattern = typeof options.security === "object" && options.security.path ? options.security.path : "/.well-known/security.txt";
79
+ injectRoute({
80
+ pattern,
81
+ entrypoint: "@rimelight/seo/routes/security.txt.ts"
82
+ });
83
+ if ((typeof options.security === "object" ? options.security.legacyRoute !== false : true) && pattern === "/.well-known/security.txt") injectRoute({
84
+ pattern: "/security.txt",
85
+ entrypoint: "@rimelight/seo/routes/security.txt.ts"
86
+ });
87
+ }
88
+ if (options.apiCatalog !== false) injectRoute({
89
+ pattern: typeof options.apiCatalog === "object" && options.apiCatalog.path ? options.apiCatalog.path : "/.well-known/api-catalog",
90
+ entrypoint: "@rimelight/seo/routes/api-catalog.ts"
91
+ });
92
+ if (options.manifest !== false) injectRoute({
93
+ pattern: typeof options.manifest === "object" && options.manifest.path ? options.manifest.path : "/site.webmanifest",
94
+ entrypoint: "@rimelight/seo/routes/site.webmanifest.ts"
95
+ });
18
96
  updateConfig({ vite: { plugins: [{
97
+ name: "vite-plugin-rimelight-seo-assets",
98
+ configureServer(server) {
99
+ if (!masterIconBuffer) return;
100
+ server.middlewares.use(async (req, res, next) => {
101
+ const url = req.url?.split("?")[0];
102
+ if (url === "/favicon.ico" && assetOpts.faviconIco !== false) {
103
+ const ico = await generateIco(masterIconBuffer, 32);
104
+ res.setHeader("Content-Type", "image/x-icon");
105
+ res.setHeader("Cache-Control", "no-cache");
106
+ res.end(ico);
107
+ return;
108
+ }
109
+ if (url === "/apple-touch-icon.png" && assetOpts.appleTouchIcon !== false) {
110
+ const png = await generatePng(masterIconBuffer, {
111
+ width: 180,
112
+ height: 180
113
+ });
114
+ res.setHeader("Content-Type", "image/png");
115
+ res.setHeader("Cache-Control", "no-cache");
116
+ res.end(png);
117
+ return;
118
+ }
119
+ if (url === "/icon-192.png" && assetOpts.icon192 !== false) {
120
+ const png = await generatePng(masterIconBuffer, {
121
+ width: 192,
122
+ height: 192
123
+ });
124
+ res.setHeader("Content-Type", "image/png");
125
+ res.setHeader("Cache-Control", "no-cache");
126
+ res.end(png);
127
+ return;
128
+ }
129
+ if (url === "/icon-512.png" && assetOpts.icon512 !== false) {
130
+ const png = await generatePng(masterIconBuffer, {
131
+ width: 512,
132
+ height: 512
133
+ });
134
+ res.setHeader("Content-Type", "image/png");
135
+ res.setHeader("Cache-Control", "no-cache");
136
+ res.end(png);
137
+ return;
138
+ }
139
+ if (url === "/icon-maskable-512.png" && assetOpts.iconMaskable !== false) {
140
+ const png = await generateMaskablePng(masterIconBuffer, {
141
+ size: 512,
142
+ background: bgColor
143
+ });
144
+ res.setHeader("Content-Type", "image/png");
145
+ res.setHeader("Cache-Control", "no-cache");
146
+ res.end(png);
147
+ return;
148
+ }
149
+ if (url === "/favicon.svg" && isSvg) {
150
+ res.setHeader("Content-Type", "image/svg+xml");
151
+ res.setHeader("Cache-Control", "no-cache");
152
+ res.end(masterIconBuffer);
153
+ return;
154
+ }
155
+ if (url === "/og.png" && assetOpts.ogImage !== false) {
156
+ const og = await generateOgCard({
157
+ logoBuffer: masterIconBuffer,
158
+ background: bgColor
159
+ });
160
+ res.setHeader("Content-Type", "image/png");
161
+ res.setHeader("Cache-Control", "no-cache");
162
+ res.end(og);
163
+ return;
164
+ }
165
+ next();
166
+ });
167
+ },
168
+ async generateBundle() {
169
+ if (options.assets === false || !masterIconBuffer) return;
170
+ if (assetOpts.faviconIco !== false) {
171
+ const ico = await generateIco(masterIconBuffer, 32);
172
+ this.emitFile({
173
+ type: "asset",
174
+ fileName: "favicon.ico",
175
+ source: ico
176
+ });
177
+ }
178
+ if (assetOpts.appleTouchIcon !== false) {
179
+ const png = await generatePng(masterIconBuffer, {
180
+ width: 180,
181
+ height: 180
182
+ });
183
+ this.emitFile({
184
+ type: "asset",
185
+ fileName: "apple-touch-icon.png",
186
+ source: png
187
+ });
188
+ }
189
+ if (assetOpts.icon192 !== false) {
190
+ const png = await generatePng(masterIconBuffer, {
191
+ width: 192,
192
+ height: 192
193
+ });
194
+ this.emitFile({
195
+ type: "asset",
196
+ fileName: "icon-192.png",
197
+ source: png
198
+ });
199
+ }
200
+ if (assetOpts.icon512 !== false) {
201
+ const png = await generatePng(masterIconBuffer, {
202
+ width: 512,
203
+ height: 512
204
+ });
205
+ this.emitFile({
206
+ type: "asset",
207
+ fileName: "icon-512.png",
208
+ source: png
209
+ });
210
+ }
211
+ if (assetOpts.iconMaskable !== false) {
212
+ const png = await generateMaskablePng(masterIconBuffer, {
213
+ size: 512,
214
+ background: bgColor
215
+ });
216
+ this.emitFile({
217
+ type: "asset",
218
+ fileName: "icon-maskable-512.png",
219
+ source: png
220
+ });
221
+ }
222
+ if (assetOpts.ogImage !== false) {
223
+ const og = await generateOgCard({
224
+ logoBuffer: masterIconBuffer,
225
+ background: bgColor
226
+ });
227
+ this.emitFile({
228
+ type: "asset",
229
+ fileName: "og.png",
230
+ source: og
231
+ });
232
+ }
233
+ if (isSvg) this.emitFile({
234
+ type: "asset",
235
+ fileName: "favicon.svg",
236
+ source: masterIconBuffer
237
+ });
238
+ }
239
+ }, {
19
240
  name: "vite-plugin-rimelight-seo",
20
241
  resolveId(id) {
21
- if (id === "virtual:rimelight-seo-config") return "\0virtual:rimelight-seo-config";
242
+ if (id === "virtual:rimelight-seo-config" || id === "virtual:rimelight-seo/options") return "\0virtual:rimelight-seo/options";
243
+ if (id === "virtual:rimelight-seo/config") return "\0virtual:rimelight-seo/config";
244
+ if (id === "virtual:rimelight-seo/site-config") return "\0virtual:rimelight-seo/site-config";
245
+ if (id === "virtual:rimelight-seo/db") return "\0virtual:rimelight-seo/db";
246
+ if (id === "virtual:rimelight-seo/corpus") return "\0virtual:rimelight-seo/corpus";
22
247
  return null;
23
248
  },
24
249
  load(id) {
25
- if (id === "\0virtual:rimelight-seo-config") return `export default ${JSON.stringify(options)};`;
250
+ if (id === "\0virtual:rimelight-seo/options") return `export default ${JSON.stringify(options)};`;
251
+ if (id === "\0virtual:rimelight-seo/config") {
252
+ if (seoConfigPath) return `import * as customConfig from ${JSON.stringify(seoConfigPath)};
253
+ export const SEO_LOCALES = customConfig.SEO_LOCALES;
254
+ export const PRIVATE_PATH_PREFIXES = customConfig.PRIVATE_PATH_PREFIXES || [];
255
+ export const seoEntries = customConfig.seoEntries;
256
+ export const rssEntries = customConfig.rssEntries;
257
+ export default customConfig;`;
258
+ return `export const SEO_LOCALES = undefined;
259
+ export const PRIVATE_PATH_PREFIXES = [];
260
+ export const seoEntries = async () => [];
261
+ export const rssEntries = undefined;`;
262
+ }
263
+ if (id === "\0virtual:rimelight-seo/site-config") {
264
+ if (siteConfigPath) return `export * from ${JSON.stringify(siteConfigPath)};`;
265
+ return `export const siteConfig = { name: "Site", description: "", url: "" };`;
266
+ }
267
+ if (id === "\0virtual:rimelight-seo/db") {
268
+ if (dbPath) return `export * from ${JSON.stringify(dbPath)};`;
269
+ return `export const db = null;
270
+ export const pages = null;`;
271
+ }
272
+ if (id === "\0virtual:rimelight-seo/corpus") {
273
+ if (corpusPath) return `export * from ${JSON.stringify(corpusPath)};`;
274
+ return `export const corpusPages = [];`;
275
+ }
26
276
  return null;
27
277
  }
28
278
  }] } });
@@ -30,4 +280,4 @@ function rimelightSeo(options = {}) {
30
280
  };
31
281
  }
32
282
  //#endregion
33
- export { rimelightSeo };
283
+ export { rimelightSeo as default, rimelightSeo };
@@ -0,0 +1,7 @@
1
+ import { WebManifest, WebManifestOptions } from "./types.mjs";
2
+ //#region src/manifest.d.ts
3
+ /**
4
+ * Build web app manifest (site.webmanifest) compliant with W3C Web App Manifest.
5
+ */
6
+ export declare function buildWebManifest(options: WebManifestOptions): WebManifest;
7
+ //#endregion
@@ -0,0 +1,44 @@
1
+ //#region src/manifest.ts
2
+ /**
3
+ * Build web app manifest (site.webmanifest) compliant with W3C Web App Manifest.
4
+ */
5
+ function buildWebManifest(options) {
6
+ const { name, shortName = name, description = "", startUrl = "/", display = "standalone", backgroundColor = "#ffffff", themeColor = "#ffffff", icons = [
7
+ {
8
+ src: "/icon-192.png",
9
+ sizes: "192x192",
10
+ type: "image/png",
11
+ purpose: "any"
12
+ },
13
+ {
14
+ src: "/icon-512.png",
15
+ sizes: "512x512",
16
+ type: "image/png",
17
+ purpose: "any"
18
+ },
19
+ {
20
+ src: "/icon-maskable-512.png",
21
+ sizes: "512x512",
22
+ type: "image/png",
23
+ purpose: "maskable"
24
+ }
25
+ ], shortcuts, categories, lang, dir, orientation, scope } = options;
26
+ return {
27
+ name,
28
+ short_name: shortName,
29
+ ...description ? { description } : {},
30
+ start_url: startUrl,
31
+ display,
32
+ background_color: backgroundColor,
33
+ theme_color: themeColor,
34
+ icons,
35
+ ...shortcuts ? { shortcuts } : {},
36
+ ...categories ? { categories } : {},
37
+ ...lang ? { lang } : {},
38
+ ...dir ? { dir } : {},
39
+ ...orientation ? { orientation } : {},
40
+ ...scope ? { scope } : {}
41
+ };
42
+ }
43
+ //#endregion
44
+ export { buildWebManifest };