@altano/astro-opengraph 1.0.0 → 2.0.0

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.
package/dist/config.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { OpengraphImageConfigDeserialized, OpengraphImageConfigResolved, OpengraphImageConfigSerializableMaybeMocked } from "./types-BGbFKpbi.js";
2
-
1
+ import { OpengraphImageConfigDeserialized, OpengraphImageConfigResolved, OpengraphImageConfigSerializableMaybeMocked } from "./types-DCSh1ad6.js";
3
2
  //#region src/config.d.ts
4
3
  declare function deserializeVirtualConfig(configMaybeMocked: OpengraphImageConfigSerializableMaybeMocked): Promise<OpengraphImageConfigDeserialized>;
5
4
  declare function getResolvedConfig(): Promise<OpengraphImageConfigResolved>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\n\nimport type {\n FontWithBuffer,\n MetaTagDefaults,\n OpengraphImageConfigDeserialized,\n OpengraphImageConfigResolved,\n OpengraphImageConfigSerializableMaybeMocked,\n} from \"./types.js\";\n\nexport async function deserializeVirtualConfig(\n configMaybeMocked: OpengraphImageConfigSerializableMaybeMocked,\n): Promise<OpengraphImageConfigDeserialized> {\n const config =\n typeof configMaybeMocked === \"function\"\n ? configMaybeMocked() // We're running in vitest which is only capable of mocking functions\n : configMaybeMocked;\n const fontsWithBuffers: FontWithBuffer[] = await Promise.all(\n config.imageOptions.fonts.map(async (font) => {\n const data = await readFile(font.path);\n return {\n ...font,\n data: data,\n };\n }),\n );\n\n return {\n ...config,\n imageOptions: {\n ...config.imageOptions,\n fonts: fontsWithBuffers,\n },\n };\n}\n\nconst imageOptionDefaults = {\n width: 1200,\n height: 630,\n} as const;\n\nconst metaTagDefaults: MetaTagDefaults = {\n \"og:image\": `/opengraph.png`,\n};\n\nexport async function getResolvedConfig(): Promise<OpengraphImageConfigResolved> {\n // Grab the virtual module that holds the integration's user config\n const module = await import(\"virtual:astro-opengraph/user-config\");\n const lazyConfig = module.default;\n const deserialized = await deserializeVirtualConfig(lazyConfig);\n return {\n ...deserialized,\n imageOptions: {\n ...imageOptionDefaults,\n ...deserialized.imageOptions,\n },\n componentMetaTagFallbacks: {\n ...deserialized.componentMetaTagFallbacks,\n ...metaTagDefaults,\n },\n };\n}\n"],"mappings":";;AAUA,eAAsB,yBACpB,mBAC2C;CAC3C,MAAM,SACJ,OAAO,sBAAsB,aACzB,mBAAmB,GACnB;CACN,MAAM,mBAAqC,MAAM,QAAQ,IACvD,OAAO,aAAa,MAAM,IAAI,OAAO,SAAS;EAC5C,MAAM,OAAO,MAAM,SAAS,KAAK,KAAK;AACtC,SAAO;GACL,GAAG;GACG;GACP;GACD,CACH;AAED,QAAO;EACL,GAAG;EACH,cAAc;GACZ,GAAG,OAAO;GACV,OAAO;GACR;EACF;;AAGH,MAAM,sBAAsB;CAC1B,OAAO;CACP,QAAQ;CACT;AAED,MAAM,kBAAmC,EACvC,YAAY,kBACb;AAED,eAAsB,oBAA2D;CAG/E,MAAM,cADS,MAAM,OAAO,wCACF;CAC1B,MAAM,eAAe,MAAM,yBAAyB,WAAW;AAC/D,QAAO;EACL,GAAG;EACH,cAAc;GACZ,GAAG;GACH,GAAG,aAAa;GACjB;EACD,2BAA2B;GACzB,GAAG,aAAa;GAChB,GAAG;GACJ;EACF"}
1
+ {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\n\nimport type {\n FontWithBuffer,\n MetaTagDefaults,\n OpengraphImageConfigDeserialized,\n OpengraphImageConfigResolved,\n OpengraphImageConfigSerializableMaybeMocked,\n} from \"./types.js\";\n\nexport async function deserializeVirtualConfig(\n configMaybeMocked: OpengraphImageConfigSerializableMaybeMocked,\n): Promise<OpengraphImageConfigDeserialized> {\n const config =\n typeof configMaybeMocked === \"function\"\n ? configMaybeMocked() // We're running in vitest which is only capable of mocking functions\n : configMaybeMocked;\n const fontsWithBuffers: FontWithBuffer[] = await Promise.all(\n config.imageOptions.fonts.map(async (font) => {\n const data = await readFile(font.path);\n return {\n ...font,\n data: data,\n };\n }),\n );\n\n return {\n ...config,\n imageOptions: {\n ...config.imageOptions,\n fonts: fontsWithBuffers,\n },\n };\n}\n\nconst imageOptionDefaults = {\n width: 1200,\n height: 630,\n} as const;\n\nconst metaTagDefaults: MetaTagDefaults = {\n \"og:image\": `/opengraph.png`,\n};\n\nexport async function getResolvedConfig(): Promise<OpengraphImageConfigResolved> {\n // Grab the virtual module that holds the integration's user config\n const module = await import(\"virtual:astro-opengraph/user-config\");\n const lazyConfig = module.default;\n const deserialized = await deserializeVirtualConfig(lazyConfig);\n return {\n ...deserialized,\n imageOptions: {\n ...imageOptionDefaults,\n ...deserialized.imageOptions,\n },\n componentMetaTagFallbacks: {\n ...deserialized.componentMetaTagFallbacks,\n ...metaTagDefaults,\n },\n };\n}\n"],"mappings":";;AAUA,eAAsB,yBACpB,mBAC2C;CAC3C,MAAM,SACJ,OAAO,sBAAsB,aACzB,kBAAkB,IAClB;CACN,MAAM,mBAAqC,MAAM,QAAQ,IACvD,OAAO,aAAa,MAAM,IAAI,OAAO,SAAS;EAC5C,MAAM,OAAO,MAAM,SAAS,KAAK,IAAI;EACrC,OAAO;GACL,GAAG;GACG;EACR;CACF,CAAC,CACH;CAEA,OAAO;EACL,GAAG;EACH,cAAc;GACZ,GAAG,OAAO;GACV,OAAO;EACT;CACF;AACF;AAEA,MAAM,sBAAsB;CAC1B,OAAO;CACP,QAAQ;AACV;AAEA,MAAM,kBAAmC,EACvC,YAAY,iBACd;AAEA,eAAsB,oBAA2D;CAG/E,MAAM,cAAa,MADE,OAAO,uCAAA,CACF;CAC1B,MAAM,eAAe,MAAM,yBAAyB,UAAU;CAC9D,OAAO;EACL,GAAG;EACH,cAAc;GACZ,GAAG;GACH,GAAG,aAAa;EAClB;EACA,2BAA2B;GACzB,GAAG,aAAa;GAChB,GAAG;EACL;CACF;AACF"}
@@ -1,29 +1,21 @@
1
- import { ImageOptions } from "./types-BGbFKpbi.js";
1
+ import { ImageOptions } from "./types-DCSh1ad6.js";
2
2
  import { APIRoute } from "astro";
3
-
4
3
  //#region src/endpoint.d.ts
5
4
  type Options = {
6
5
  /**
7
- * We don't just type this as `AstroComponentFactory` because astro typing is
8
- * hard to get right and simply importing a .astro file doesn't give you
9
- * something typed as a factory.
10
- */
6
+ * We don't just type this as `AstroComponentFactory` because astro typing is
7
+ * hard to get right and simply importing a .astro file doesn't give you
8
+ * something typed as a factory.
9
+ */
11
10
  template: unknown;
12
11
  /**
13
- * Custom "Content-Type" header, only used in SSR. Defaults to "image/png"
14
- */
12
+ * Custom "Content-Type" header, only used in SSR. Defaults to "image/png"
13
+ */
15
14
  contentType?: string;
16
15
  imageOptionOverrides?: Partial<ImageOptions>;
17
16
  };
18
- declare function makeOpengraphDevEndpoint({
19
- template,
20
- contentType
21
- }: Options): APIRoute;
22
- declare function makeOpengraphEndpoint({
23
- template,
24
- imageOptionOverrides,
25
- contentType
26
- }: Options): APIRoute;
17
+ declare function makeOpengraphDevEndpoint({ template, contentType }: Options): APIRoute;
18
+ declare function makeOpengraphEndpoint({ template, imageOptionOverrides, contentType }: Options): APIRoute;
27
19
  //#endregion
28
20
  export { makeOpengraphDevEndpoint, makeOpengraphEndpoint };
29
21
  //# sourceMappingURL=endpoint.d.ts.map
package/dist/endpoint.js CHANGED
@@ -21,7 +21,10 @@ async function bufferToTransformedPNG(imageBuffer) {
21
21
  return (await getConfiguredImageService()).transform(imageBuffer, {
22
22
  src: "",
23
23
  format: "png"
24
- }, imageConfig);
24
+ }, {
25
+ ...imageConfig,
26
+ dangerouslyProcessSVG: true
27
+ });
25
28
  }
26
29
  //#endregion
27
30
  //#region src/endpoint.ts
@@ -1 +1 @@
1
- {"version":3,"file":"endpoint.js","names":["htmlToVNode","html"],"sources":["../src/image.ts","../src/endpoint.ts"],"sourcesContent":["import { getConfiguredImageService, imageConfig } from \"astro:assets\";\nimport satori, { type SatoriOptions } from \"satori\";\nimport { html as htmlToVNode } from \"satori-html\";\nimport he from \"he\";\n\nimport type { LocalImageService } from \"astro\";\n\n/**\n * Convert an html string to an image\n */\nexport async function htmlToPNG(\n html: string,\n satoriOptions: SatoriOptions,\n): Promise<ReturnType<LocalImageService[\"transform\"]>> {\n // html text => vnode\n const responseTextWithDecodedHtmlEntities = he.decode(html);\n const vnode = htmlToVNode(responseTextWithDecodedHtmlEntities);\n\n // vnode => svg\n const svg = await satori(vnode as React.ReactNode, satoriOptions);\n\n // svg => buffer\n const fileBuffer = Buffer.from(svg, \"utf-8\");\n const uint8Array = new Uint8Array(\n fileBuffer.buffer,\n fileBuffer.byteOffset,\n fileBuffer.byteLength,\n );\n\n // buffer => transformed png\n return bufferToTransformedPNG(uint8Array);\n}\n\n/**\n * Convert a buffer to an image using the astro local image service\n */\nasync function bufferToTransformedPNG(\n imageBuffer: Uint8Array,\n): ReturnType<LocalImageService[\"transform\"]> {\n const imageService = (await getConfiguredImageService()) as LocalImageService;\n\n return imageService.transform(\n imageBuffer,\n { src: \"\", format: \"png\" },\n imageConfig,\n );\n}\n","import type { APIRoute } from \"astro\";\nimport { experimental_AstroContainer } from \"astro/container\";\nimport { isAstroComponentFactory } from \"astro/runtime/server/render/astro/factory.js\";\nimport { getResolvedConfig } from \"./config.js\";\nimport type { ImageOptions } from \"./types.js\";\nimport { htmlToPNG } from \"./image.js\";\n\ntype Options = {\n /**\n * We don't just type this as `AstroComponentFactory` because astro typing is\n * hard to get right and simply importing a .astro file doesn't give you\n * something typed as a factory.\n */\n template: unknown;\n /**\n * Custom \"Content-Type\" header, only used in SSR. Defaults to \"image/png\"\n */\n contentType?: string;\n imageOptionOverrides?: Partial<ImageOptions>;\n};\n\nexport function makeOpengraphDevEndpoint({\n template,\n contentType = \"text/html\",\n}: Options): APIRoute {\n return async ({ locals, params, props, request }) => {\n const config = await getResolvedConfig();\n if (config.command !== \"dev\") {\n return new Response(null, { status: 404 });\n }\n\n if (!isAstroComponentFactory(template)) {\n throw new Error(`Given is not an Astro template.`);\n }\n\n const container = await experimental_AstroContainer.create();\n const templateStr = await container.renderToString(template, {\n partial: false,\n request,\n params,\n locals,\n props,\n });\n\n return new Response(templateStr, {\n headers: {\n \"Content-Type\": contentType,\n },\n });\n };\n}\n\nexport function makeOpengraphEndpoint({\n template,\n imageOptionOverrides,\n contentType = \"image/png\",\n}: Options): APIRoute {\n return async ({ locals, params, props, request }) => {\n if (!isAstroComponentFactory(template)) {\n throw new Error(`Given is not an Astro template.`);\n }\n\n const container = await experimental_AstroContainer.create();\n const templateStr = await container.renderToString(template, {\n partial: false,\n request,\n params,\n locals,\n props,\n });\n\n const config = await getResolvedConfig();\n const imageOptions = {\n ...config.imageOptions,\n ...imageOptionOverrides,\n };\n const png = await htmlToPNG(templateStr, imageOptions);\n\n return new Response(new Uint8Array(png.data), {\n headers: {\n \"Content-Type\": contentType,\n },\n });\n };\n}\n"],"mappings":";;;;;;;;;;;AAUA,eAAsB,UACpB,QACA,eACqD;CAMrD,MAAM,MAAM,MAAM,OAHJA,KAD8B,GAAG,OAAOC,OAAK,CACG,EAGX,cAAc;CAGjE,MAAM,aAAa,OAAO,KAAK,KAAK,QAAQ;AAQ5C,QAAO,uBAPY,IAAI,WACrB,WAAW,QACX,WAAW,YACX,WAAW,WACZ,CAGwC;;;;;AAM3C,eAAe,uBACb,aAC4C;AAG5C,SAFsB,MAAM,2BAA2B,EAEnC,UAClB,aACA;EAAE,KAAK;EAAI,QAAQ;EAAO,EAC1B,YACD;;;;ACxBH,SAAgB,yBAAyB,EACvC,UACA,cAAc,eACM;AACpB,QAAO,OAAO,EAAE,QAAQ,QAAQ,OAAO,cAAc;AAEnD,OADe,MAAM,mBAAmB,EAC7B,YAAY,MACrB,QAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,KAAK,CAAC;AAG5C,MAAI,CAAC,wBAAwB,SAAS,CACpC,OAAM,IAAI,MAAM,kCAAkC;EAIpD,MAAM,cAAc,OADF,MAAM,4BAA4B,QAAQ,EACxB,eAAe,UAAU;GAC3D,SAAS;GACT;GACA;GACA;GACA;GACD,CAAC;AAEF,SAAO,IAAI,SAAS,aAAa,EAC/B,SAAS,EACP,gBAAgB,aACjB,EACF,CAAC;;;AAIN,SAAgB,sBAAsB,EACpC,UACA,sBACA,cAAc,eACM;AACpB,QAAO,OAAO,EAAE,QAAQ,QAAQ,OAAO,cAAc;AACnD,MAAI,CAAC,wBAAwB,SAAS,CACpC,OAAM,IAAI,MAAM,kCAAkC;EAiBpD,MAAM,MAAM,MAAM,UAbE,OADF,MAAM,4BAA4B,QAAQ,EACxB,eAAe,UAAU;GAC3D,SAAS;GACT;GACA;GACA;GACA;GACD,CAAC,EAGmB;GACnB,IAFa,MAAM,mBAAmB,EAE5B;GACV,GAAG;GACJ,CACqD;AAEtD,SAAO,IAAI,SAAS,IAAI,WAAW,IAAI,KAAK,EAAE,EAC5C,SAAS,EACP,gBAAgB,aACjB,EACF,CAAC"}
1
+ {"version":3,"file":"endpoint.js","names":["htmlToVNode","html"],"sources":["../src/image.ts","../src/endpoint.ts"],"sourcesContent":["import { getConfiguredImageService, imageConfig } from \"astro:assets\";\nimport satori, { type SatoriOptions } from \"satori\";\nimport { html as htmlToVNode } from \"satori-html\";\nimport he from \"he\";\n\nimport type { LocalImageService } from \"astro\";\n\n/**\n * Convert an html string to an image\n */\nexport async function htmlToPNG(\n html: string,\n satoriOptions: SatoriOptions,\n): Promise<ReturnType<LocalImageService[\"transform\"]>> {\n // html text => vnode\n const responseTextWithDecodedHtmlEntities = he.decode(html);\n const vnode = htmlToVNode(responseTextWithDecodedHtmlEntities);\n\n // vnode => svg\n const svg = await satori(vnode as React.ReactNode, satoriOptions);\n\n // svg => buffer\n const fileBuffer = Buffer.from(svg, \"utf-8\");\n const uint8Array = new Uint8Array(\n fileBuffer.buffer,\n fileBuffer.byteOffset,\n fileBuffer.byteLength,\n );\n\n // buffer => transformed png\n return bufferToTransformedPNG(uint8Array);\n}\n\n/**\n * Convert a buffer to an image using the astro local image service\n */\nasync function bufferToTransformedPNG(\n imageBuffer: Uint8Array,\n): ReturnType<LocalImageService[\"transform\"]> {\n const imageService = (await getConfiguredImageService()) as LocalImageService;\n\n // Astro 7 disables SVG rasterization in the sharp image service by default\n // (`image.dangerouslyProcessSVG`). Our input is an SVG we produced ourselves\n // with satori, so enable rasterization for *this transform only* by passing a\n // scoped config override. We intentionally do NOT flip the global\n // `image.dangerouslyProcessSVG` config, which is a project-wide security\n // decision the user must make for the other SVGs they process.\n return imageService.transform(\n imageBuffer,\n { src: \"\", format: \"png\" },\n { ...imageConfig, dangerouslyProcessSVG: true },\n );\n}\n","import type { APIRoute } from \"astro\";\nimport { experimental_AstroContainer } from \"astro/container\";\nimport { isAstroComponentFactory } from \"astro/runtime/server/render/astro/factory.js\";\nimport { getResolvedConfig } from \"./config.js\";\nimport type { ImageOptions } from \"./types.js\";\nimport { htmlToPNG } from \"./image.js\";\n\ntype Options = {\n /**\n * We don't just type this as `AstroComponentFactory` because astro typing is\n * hard to get right and simply importing a .astro file doesn't give you\n * something typed as a factory.\n */\n template: unknown;\n /**\n * Custom \"Content-Type\" header, only used in SSR. Defaults to \"image/png\"\n */\n contentType?: string;\n imageOptionOverrides?: Partial<ImageOptions>;\n};\n\nexport function makeOpengraphDevEndpoint({\n template,\n contentType = \"text/html\",\n}: Options): APIRoute {\n return async ({ locals, params, props, request }) => {\n const config = await getResolvedConfig();\n if (config.command !== \"dev\") {\n return new Response(null, { status: 404 });\n }\n\n if (!isAstroComponentFactory(template)) {\n throw new Error(`Given is not an Astro template.`);\n }\n\n const container = await experimental_AstroContainer.create();\n const templateStr = await container.renderToString(template, {\n partial: false,\n request,\n params,\n locals,\n props,\n });\n\n return new Response(templateStr, {\n headers: {\n \"Content-Type\": contentType,\n },\n });\n };\n}\n\nexport function makeOpengraphEndpoint({\n template,\n imageOptionOverrides,\n contentType = \"image/png\",\n}: Options): APIRoute {\n return async ({ locals, params, props, request }) => {\n if (!isAstroComponentFactory(template)) {\n throw new Error(`Given is not an Astro template.`);\n }\n\n const container = await experimental_AstroContainer.create();\n const templateStr = await container.renderToString(template, {\n partial: false,\n request,\n params,\n locals,\n props,\n });\n\n const config = await getResolvedConfig();\n const imageOptions = {\n ...config.imageOptions,\n ...imageOptionOverrides,\n };\n const png = await htmlToPNG(templateStr, imageOptions);\n\n return new Response(new Uint8Array(png.data), {\n headers: {\n \"Content-Type\": contentType,\n },\n });\n };\n}\n"],"mappings":";;;;;;;;;;;AAUA,eAAsB,UACpB,QACA,eACqD;CAMrD,MAAM,MAAM,MAAM,OAHJA,KAD8B,GAAG,OAAOC,MACM,CAG/B,GAAsB,aAAa;CAGhE,MAAM,aAAa,OAAO,KAAK,KAAK,OAAO;CAQ3C,OAAO,uBAAuB,IAPP,WACrB,WAAW,QACX,WAAW,YACX,WAAW,UAI0B,CAAC;AAC1C;;;;AAKA,eAAe,uBACb,aAC4C;CAS5C,QAAO,MARqB,0BAA0B,EAAA,CAQlC,UAClB,aACA;EAAE,KAAK;EAAI,QAAQ;CAAM,GACzB;EAAE,GAAG;EAAa,uBAAuB;CAAK,CAChD;AACF;;;AC/BA,SAAgB,yBAAyB,EACvC,UACA,cAAc,eACM;CACpB,OAAO,OAAO,EAAE,QAAQ,QAAQ,OAAO,cAAc;EAEnD,KAAI,MADiB,kBAAkB,EAAA,CAC5B,YAAY,OACrB,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;EAG3C,IAAI,CAAC,wBAAwB,QAAQ,GACnC,MAAM,IAAI,MAAM,iCAAiC;EAInD,MAAM,cAAc,OAAM,MADF,4BAA4B,OAAO,EAAA,CACvB,eAAe,UAAU;GAC3D,SAAS;GACT;GACA;GACA;GACA;EACF,CAAC;EAED,OAAO,IAAI,SAAS,aAAa,EAC/B,SAAS,EACP,gBAAgB,YAClB,EACF,CAAC;CACH;AACF;AAEA,SAAgB,sBAAsB,EACpC,UACA,sBACA,cAAc,eACM;CACpB,OAAO,OAAO,EAAE,QAAQ,QAAQ,OAAO,cAAc;EACnD,IAAI,CAAC,wBAAwB,QAAQ,GACnC,MAAM,IAAI,MAAM,iCAAiC;EAiBnD,MAAM,MAAM,MAAM,UAAU,OAbF,MADF,4BAA4B,OAAO,EAAA,CACvB,eAAe,UAAU;GAC3D,SAAS;GACT;GACA;GACA;GACA;EACF,CAAC,GAOwC;GAHvC,IAAG,MAFgB,kBAAkB,EAAA,CAE3B;GACV,GAAG;EAE+C,CAAC;EAErD,OAAO,IAAI,SAAS,IAAI,WAAW,IAAI,IAAI,GAAG,EAC5C,SAAS,EACP,gBAAgB,YAClB,EACF,CAAC;CACH;AACF"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { ComponentMetaTagFallbacks, ImageOptionsWithFontPaths, OpengraphImageConfig } from "./types-BGbFKpbi.js";
1
+ import { ComponentMetaTagFallbacks, ImageOptionsWithFontPaths, OpengraphImageConfig } from "./types-DCSh1ad6.js";
2
2
  import { AstroIntegration } from "astro";
3
-
4
3
  //#region src/integration.d.ts
5
4
  declare const _default: (config: OpengraphImageConfig) => AstroIntegration;
6
5
  //#endregion
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/virtual-user-config.ts","../src/integration.ts"],"sourcesContent":["import type { ViteUserConfig } from \"astro\";\n\nimport type { OpengraphImageConfigSerializable } from \"./types.js\";\n\nfunction resolveVirtualModuleId<T extends string>(id: T): `\\0${T}` {\n return `\\0${id}`;\n}\n\n/**\n * Vite plugin that exposes OpengraphImage user config and project context via\n * virtual modules.\n */\nexport function vitePluginOpengraphImageUserConfig(\n config: OpengraphImageConfigSerializable,\n): NonNullable<ViteUserConfig[\"plugins\"]>[number] {\n /** Map of virtual module names to their code contents as strings. */\n const modules = {\n \"virtual:astro-opengraph/user-config\": `export default ${JSON.stringify(\n config,\n )}`,\n } satisfies Record<string, string>;\n\n /** Mapping names prefixed with `\\0` to their original form. */\n const resolutionMap = Object.fromEntries(\n (Object.keys(modules) as (keyof typeof modules)[]).map((key) => [\n resolveVirtualModuleId(key),\n key,\n ]),\n );\n\n return {\n name: \"vite-plugin-astro-opengraph-user-config\",\n resolveId(id): string | void {\n if (id in modules) return resolveVirtualModuleId(id);\n },\n load(id): string | void {\n const resolution = resolutionMap[id];\n if (resolution) return modules[resolution];\n },\n };\n}\n","import type { AstroIntegration } from \"astro\";\nimport type {\n OpengraphImageConfig,\n OpengraphImageConfigSerializable,\n} from \"./types.js\";\nimport { vitePluginOpengraphImageUserConfig } from \"./virtual-user-config.js\";\n\nexport default (config: OpengraphImageConfig): AstroIntegration => ({\n name: \"@altano/astro-opengraph\",\n hooks: {\n async \"astro:config:setup\"({\n updateConfig,\n command,\n addDevToolbarApp,\n }): Promise<void> {\n const imageOptions = await config.getImageOptions();\n const configSerializable: OpengraphImageConfigSerializable = {\n imageOptions,\n command,\n };\n updateConfig({\n vite: {\n // Shove the serializable config into the virtual module for later\n // retrieval in the middleware\n plugins: [vitePluginOpengraphImageUserConfig(configSerializable)],\n },\n });\n addDevToolbarApp({\n id: \"@altano/astro-opengraph\",\n name: \"Open Graph\",\n // icon: \"🌠\",\n icon: \"image\",\n // We don't use `@altano/astro-opengraph/toolbar` because that won't\n // resolve when serving the test fixture for e2e runs (`pnpm\n // test:e2e:server`)\n entrypoint: new URL(\"./toolbar/index.js\", import.meta.url),\n });\n },\n },\n});\n"],"mappings":";AAIA,SAAS,uBAAyC,IAAiB;AACjE,QAAO,KAAK;;;;;;AAOd,SAAgB,mCACd,QACgD;;CAEhD,MAAM,UAAU,EACd,uCAAuC,kBAAkB,KAAK,UAC5D,OACD,IACF;;CAGD,MAAM,gBAAgB,OAAO,YAC1B,OAAO,KAAK,QAAQ,CAA8B,KAAK,QAAQ,CAC9D,uBAAuB,IAAI,EAC3B,IACD,CAAC,CACH;AAED,QAAO;EACL,MAAM;EACN,UAAU,IAAmB;AAC3B,OAAI,MAAM,QAAS,QAAO,uBAAuB,GAAG;;EAEtD,KAAK,IAAmB;GACtB,MAAM,aAAa,cAAc;AACjC,OAAI,WAAY,QAAO,QAAQ;;EAElC;;;;AChCH,IAAA,uBAAgB,YAAoD;CAClE,MAAM;CACN,OAAO,EACL,MAAM,qBAAqB,EACzB,cACA,SACA,oBACgB;AAMhB,eAAa,EACX,MAAM,EAGJ,SAAS,CAAC,mCAR+C;GAC3D,cAFmB,MAAM,OAAO,iBAAiB;GAGjD;GACD,CAKmE,CAAC,EAClE,EACF,CAAC;AACF,mBAAiB;GACf,IAAI;GACJ,MAAM;GAEN,MAAM;GAIN,YAAY,IAAI,IAAI,sBAAsB,OAAO,KAAK,IAAI;GAC3D,CAAC;IAEL;CACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/virtual-user-config.ts","../src/integration.ts"],"sourcesContent":["import type { ViteUserConfig } from \"astro\";\n\nimport type { OpengraphImageConfigSerializable } from \"./types.js\";\n\nfunction resolveVirtualModuleId<T extends string>(id: T): `\\0${T}` {\n return `\\0${id}`;\n}\n\n/**\n * Vite plugin that exposes OpengraphImage user config and project context via\n * virtual modules.\n */\nexport function vitePluginOpengraphImageUserConfig(\n config: OpengraphImageConfigSerializable,\n): NonNullable<ViteUserConfig[\"plugins\"]>[number] {\n /** Map of virtual module names to their code contents as strings. */\n const modules = {\n \"virtual:astro-opengraph/user-config\": `export default ${JSON.stringify(\n config,\n )}`,\n } satisfies Record<string, string>;\n\n /** Mapping names prefixed with `\\0` to their original form. */\n const resolutionMap = Object.fromEntries(\n (Object.keys(modules) as (keyof typeof modules)[]).map((key) => [\n resolveVirtualModuleId(key),\n key,\n ]),\n );\n\n return {\n name: \"vite-plugin-astro-opengraph-user-config\",\n resolveId(id): string | void {\n if (id in modules) return resolveVirtualModuleId(id);\n },\n load(id): string | void {\n const resolution = resolutionMap[id];\n if (resolution) return modules[resolution];\n },\n };\n}\n","import type { AstroIntegration } from \"astro\";\nimport type {\n OpengraphImageConfig,\n OpengraphImageConfigSerializable,\n} from \"./types.js\";\nimport { vitePluginOpengraphImageUserConfig } from \"./virtual-user-config.js\";\n\nexport default (config: OpengraphImageConfig): AstroIntegration => ({\n name: \"@altano/astro-opengraph\",\n hooks: {\n async \"astro:config:setup\"({\n updateConfig,\n command,\n addDevToolbarApp,\n }): Promise<void> {\n const imageOptions = await config.getImageOptions();\n const configSerializable: OpengraphImageConfigSerializable = {\n imageOptions,\n command,\n };\n updateConfig({\n vite: {\n // Shove the serializable config into the virtual module for later\n // retrieval in the middleware\n plugins: [vitePluginOpengraphImageUserConfig(configSerializable)],\n },\n });\n addDevToolbarApp({\n id: \"@altano/astro-opengraph\",\n name: \"Open Graph\",\n // icon: \"🌠\",\n icon: \"image\",\n // We don't use `@altano/astro-opengraph/toolbar` because that won't\n // resolve when serving the test fixture for e2e runs (`pnpm\n // test:e2e:server`)\n entrypoint: new URL(\"./toolbar/index.js\", import.meta.url),\n });\n },\n },\n});\n"],"mappings":";AAIA,SAAS,uBAAyC,IAAiB;CACjE,OAAO,KAAK;AACd;;;;;AAMA,SAAgB,mCACd,QACgD;;CAEhD,MAAM,UAAU,EACd,uCAAuC,kBAAkB,KAAK,UAC5D,MACF,IACF;;CAGA,MAAM,gBAAgB,OAAO,YAC1B,OAAO,KAAK,OAAO,CAAC,CAA8B,KAAK,QAAQ,CAC9D,uBAAuB,GAAG,GAC1B,GACF,CAAC,CACH;CAEA,OAAO;EACL,MAAM;EACN,UAAU,IAAmB;GAC3B,IAAI,MAAM,SAAS,OAAO,uBAAuB,EAAE;EACrD;EACA,KAAK,IAAmB;GACtB,MAAM,aAAa,cAAc;GACjC,IAAI,YAAY,OAAO,QAAQ;EACjC;CACF;AACF;;;ACjCA,IAAA,uBAAgB,YAAoD;CAClE,MAAM;CACN,OAAO,EACL,MAAM,qBAAqB,EACzB,cACA,SACA,oBACgB;EAMhB,aAAa,EACX,MAAM,EAGJ,SAAS,CAAC,mCAAmC;GAP/C,cAAA,MAFyB,OAAO,gBAAgB;GAGhD;EAMgE,CAAC,CAAC,EAClE,EACF,CAAC;EACD,iBAAiB;GACf,IAAI;GACJ,MAAM;GAEN,MAAM;GAIN,YAAY,IAAI,IAAI,sBAAsB,OAAO,KAAK,GAAG;EAC3D,CAAC;CACH,EACF;AACF"}
@@ -1,5 +1,4 @@
1
1
  import { DevToolbarApp } from "astro";
2
-
3
2
  //#region src/toolbar/index.d.ts
4
3
  declare const app: DevToolbarApp;
5
4
  //#endregion
@@ -1,6 +1,6 @@
1
1
  import { render } from "preact";
2
2
  import { defineToolbarApp } from "astro/toolbar";
3
- import { useCallback, useEffect, useRef, useState } from "preact/hooks";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks";
4
4
  import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
5
5
  //#region src/toolbar/styles.tsx
6
6
  const styles = `
@@ -81,25 +81,26 @@ function ExternalLinkicon({ width = 27, height = 27, fill = "#020202" }) {
81
81
  //#endregion
82
82
  //#region src/toolbar/components/LinkOrText.tsx
83
83
  function LinkOrText({ maybeLinkText }) {
84
- let parsedMaybeLink = null;
84
+ if (maybeLinkText == null) return null;
85
+ let url;
85
86
  try {
86
- if (maybeLinkText != null) parsedMaybeLink = /* @__PURE__ */ jsxs("a", {
87
- href: new URL(maybeLinkText).toString(),
88
- target: "_blank",
89
- children: [
90
- maybeLinkText,
91
- " ",
92
- /* @__PURE__ */ jsx(ExternalLinkicon, {
93
- width: 10,
94
- height: 10,
95
- fill: "white"
96
- })
97
- ]
98
- });
87
+ url = new URL(maybeLinkText);
99
88
  } catch (_) {
100
- parsedMaybeLink = maybeLinkText;
89
+ return maybeLinkText;
101
90
  }
102
- return parsedMaybeLink;
91
+ return /* @__PURE__ */ jsxs("a", {
92
+ href: url.toString(),
93
+ target: "_blank",
94
+ children: [
95
+ maybeLinkText,
96
+ " ",
97
+ /* @__PURE__ */ jsx(ExternalLinkicon, {
98
+ width: 10,
99
+ height: 10,
100
+ fill: "white"
101
+ })
102
+ ]
103
+ });
103
104
  }
104
105
  //#endregion
105
106
  //#region src/toolbar/hooks/useMetaTagInfo.tsx
@@ -121,13 +122,15 @@ const useHeadObserver = () => {
121
122
  return headRef;
122
123
  };
123
124
  function useMetaTagInfo() {
124
- const opengraphMetaTags = useHeadObserver()?.current?.querySelectorAll("meta[property^='og:']") ?? [];
125
+ const headRef = useHeadObserver();
126
+ const opengraphMetaTags = useMemo(() => {
127
+ return headRef?.current?.querySelectorAll("meta[property^='og:']") ?? [];
128
+ }, [headRef]);
125
129
  return Array.from(opengraphMetaTags).map((tag) => [tag.getAttribute("content"), tag.getAttribute("property")]);
126
130
  }
127
131
  //#endregion
128
132
  //#region src/toolbar/components/MetaTags.tsx
129
133
  function MetaTags() {
130
- const tags = useMetaTagInfo();
131
134
  return /* @__PURE__ */ jsx("dl", {
132
135
  "data-testid": "astro-opengraph-toolbar",
133
136
  style: {
@@ -137,7 +140,7 @@ function MetaTags() {
137
140
  gap: 7,
138
141
  columnGap: 13
139
142
  },
140
- children: tags.map((tag) => /* @__PURE__ */ jsx(MetaTagContent, { tag }, tag[1]))
143
+ children: useMetaTagInfo().map((tag) => /* @__PURE__ */ jsx(MetaTagContent, { tag }, tag[1]))
141
144
  });
142
145
  }
143
146
  function MetaTagContent({ tag }) {
@@ -281,12 +284,10 @@ function ImageZoomToolbar({ isHovered, imageSize, setImageSize }) {
281
284
  function CopyImageUrlButton() {
282
285
  const imageUrl = useImageURL();
283
286
  const [wasRecentlyClicked, setWasRecentlyClicked] = useState(false);
284
- const checkTimer = useRef(null);
285
287
  useEffect(() => {
286
- if (wasRecentlyClicked && checkTimer.current == null) checkTimer.current = setTimeout(() => {
287
- setWasRecentlyClicked(false);
288
- checkTimer.current = null;
289
- }, 3e3);
288
+ if (!wasRecentlyClicked) return;
289
+ const timer = setTimeout(() => setWasRecentlyClicked(false), 3e3);
290
+ return () => clearTimeout(timer);
290
291
  }, [wasRecentlyClicked]);
291
292
  const handleClick = useCallback((evt) => {
292
293
  navigator.clipboard.writeText(imageUrl);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Error","Error"],"sources":["../../src/toolbar/styles.tsx","../../src/toolbar/components/Summary.tsx","../../src/toolbar/components/ExternalLinkicon.tsx","../../src/toolbar/components/LinkOrText.tsx","../../src/toolbar/hooks/useMetaTagInfo.tsx","../../src/toolbar/components/MetaTags.tsx","../../src/toolbar/hooks/useHoverState.tsx","../../src/toolbar/hooks/useImageURL.tsx","../../src/toolbar/components/Image.tsx","../../src/toolbar/components/ZoomLabel.tsx","../../src/toolbar/components/ZoomButton.tsx","../../src/toolbar/components/ToolbarSection.tsx","../../src/toolbar/components/ImageZoomToolbar.tsx","../../src/toolbar/components/CopyImageUrlButton.tsx","../../src/toolbar/components/ImageSection.tsx","../../src/toolbar/components/Error.tsx","../../src/toolbar/components/getRelativeUrl.tsx","../../src/toolbar/components/ToolbarApp.tsx","../../src/toolbar/components/ToolbarAppWindow.tsx","../../src/toolbar/index.tsx"],"sourcesContent":["export const styles = /* css */ `\n :host astro-dev-toolbar-window {\n max-height: calc(100vh - 80px);\n overflow-y: auto;\n }\n\n button {\n background: none;\n color: inherit;\n border: none;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n\n details {\n margin-block-end: 10px;\n }\n details:last-of-type {\n margin-block-end: 0;\n }\n\n details[open] summary, .image {\n margin-block-end: 7px;\n }\n\n .image img {\n transition: max-width 0.1s;\n }\n`;\n","import type Preact from \"preact\";\n\nexport function Summary({\n children,\n}: {\n children: string;\n}): Preact.JSX.Element {\n return (\n <summary\n style={{\n cursor: \"pointer\",\n fontWeight: 700,\n color: \"white\",\n }}\n >\n {children}\n </summary>\n );\n}\n","import type Preact from \"preact\";\n\nexport function ExternalLinkicon({\n width = 27,\n height = 27,\n fill = \"#020202\",\n}: {\n width?: number;\n height?: number;\n fill?: string;\n}): Preact.JSX.Element {\n return (\n <span\n style={{\n display: \"inline-block\",\n width,\n height,\n }}\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 120 120\">\n <path d=\"M120.09 16.696H60.256V.096L120.09.09z\" fill={fill} />\n <path\n d=\"M57.99 48.64l42.573-42.574 13.475 13.475-42.574 42.58z\"\n fill={fill}\n />\n <path\n d=\"M119.98.107l.02 59.83-16.59.013-.02-59.846zM3 23.5h17v87H3zm83.49 52.56h17V113h-17z\"\n fill={fill}\n />\n <path d=\"M3 16.692h40.655v17H3zM3 96h100.49v17H3z\" fill={fill} />\n </svg>\n </span>\n );\n}\n","import type Preact from \"preact\";\nimport { ExternalLinkicon } from \"./ExternalLinkicon.js\";\n\ntype LinkOrTextType = Preact.JSX.Element | string | null;\n\nexport function LinkOrText({\n maybeLinkText,\n}: {\n maybeLinkText: string | null;\n}): LinkOrTextType {\n let parsedMaybeLink: LinkOrTextType = null;\n try {\n if (maybeLinkText != null) {\n const url = new URL(maybeLinkText);\n parsedMaybeLink = (\n <a href={url.toString()} target=\"_blank\">\n {maybeLinkText}{\" \"}\n <ExternalLinkicon width={10} height={10} fill=\"white\" />\n </a>\n );\n }\n } catch (_) {\n parsedMaybeLink = maybeLinkText;\n }\n return parsedMaybeLink;\n}\n","import { useEffect, useRef, useState } from \"preact/hooks\";\n\nexport type MetaTagInfo = [content: string | null, property: string | null];\n\nconst useHeadObserver = (): React.RefObject<HTMLHeadElement | null> => {\n const [_generation, setGeneration] = useState<number>(0);\n const forceRerender = (): void => setGeneration((old) => ++old);\n const headRef = useRef(document.querySelector(\"head\"));\n\n // useEffect(() => {\n // console.log({ generation });\n // }, [generation]);\n\n useEffect(() => {\n // console.log({ \"headRef.current\": headRef.current });\n if (headRef.current == null) {\n // assume we must have a head by now, and if we don't, we will never get\n // one.\n return;\n }\n const observer = new MutationObserver(forceRerender);\n observer.observe(headRef.current, {\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n });\n return () => observer.disconnect();\n }, []);\n\n return headRef;\n};\n\nexport function useMetaTagInfo(): MetaTagInfo[] {\n const headRef = useHeadObserver();\n const opengraphMetaTags =\n headRef?.current?.querySelectorAll(\"meta[property^='og:']\") ?? [];\n // console.log({\n // opengraphMetaTags: opengraphMetaTags,\n // opengraphMetaTagsLength: opengraphMetaTags.length,\n // });\n return Array.from(opengraphMetaTags).map(\n (tag: Element): MetaTagInfo => [\n tag.getAttribute(\"content\"),\n tag.getAttribute(\"property\"),\n ],\n );\n}\n","import type Preact from \"preact\";\nimport { LinkOrText } from \"./LinkOrText.js\";\nimport { useMetaTagInfo, type MetaTagInfo } from \"../hooks/useMetaTagInfo.js\";\n\nexport function MetaTags(): Preact.JSX.Element {\n const tags = useMetaTagInfo();\n\n return (\n <dl\n data-testid=\"astro-opengraph-toolbar\"\n style={{\n margin: 0,\n display: \"grid\",\n gridTemplateColumns: \"max-content 1fr\",\n gap: 7,\n columnGap: 13,\n }}\n >\n {tags.map((tag) => (\n <MetaTagContent key={tag[1]} tag={tag} />\n ))}\n </dl>\n );\n}\n\nfunction MetaTagContent({ tag }: { tag: MetaTagInfo }): Preact.JSX.Element {\n const [content, property] = tag;\n return (\n <>\n <dt\n style={{\n color: \"white\",\n textAlign: \"right\",\n fontWeight: 500,\n }}\n >\n {property}\n </dt>\n <dd style={{ margin: 0 }}>\n <LinkOrText maybeLinkText={content} />\n </dd>\n </>\n );\n}\n","import type Preact from \"preact\";\nimport { useRef, useEffect } from \"preact/hooks\";\n\nexport function useHoverState<TElement extends HTMLElement>(\n setHoverState: (isHover: boolean) => void,\n): Preact.RefObject<TElement> {\n const ref = useRef<TElement>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n const { signal } = controller;\n ref.current?.addEventListener(\"mouseenter\", () => setHoverState(true), {\n signal,\n });\n ref.current?.addEventListener(\"mouseleave\", () => setHoverState(false), {\n signal,\n });\n\n return () => controller.abort();\n }, [setHoverState]);\n\n return ref;\n}\n","import { useState } from \"preact/hooks\";\n\nexport function useImageURL(): string {\n const imageUrl = useImageURLIfAvailable();\n if (imageUrl == null) {\n throw new Error(\n `Shouldn't be rendering this component without an image URL`,\n );\n }\n return imageUrl;\n}\n\nexport function useImageURLIfAvailable(): string | null {\n const [imageUrlRef] = useState(() => {\n const meta = document.querySelector('meta[property=\"og:image\"]');\n return meta?.getAttribute(\"content\") ?? null;\n });\n return imageUrlRef;\n}\n","import type Preact from \"preact\";\nimport { useImageURL } from \"../hooks/useImageURL.js\";\n\nexport function Image({\n imageSize,\n handleLoadError,\n}: {\n imageSize: number;\n handleLoadError: () => void;\n}): Preact.JSX.Element {\n const imageUrl = useImageURL();\n return (\n <div className=\"image\">\n <a href={imageUrl} target=\"_blank\" title=\"Open image in new tab\">\n <img\n onError={handleLoadError}\n alt=\"Open Graph\"\n src={imageUrl}\n style={{\n maxHeight: \"100%\",\n maxWidth: `${imageSize}%`,\n }}\n />\n </a>\n </div>\n );\n}\n","export function ZoomLabel({ imageSize }: { imageSize: number }): string {\n const formattedNumber = (imageSize / 100).toLocaleString(undefined, {\n minimumFractionDigits: 2,\n });\n return `${formattedNumber}x`;\n}\n","import type Preact from \"preact\";\nimport { ZoomLabel } from \"./ZoomLabel.js\";\n\nexport function ZoomButton({\n value,\n imageSize,\n setImageSize,\n}: {\n value: number;\n imageSize: number;\n setImageSize: (size: number) => void;\n}): Preact.JSX.Element {\n const isSelected = value === imageSize;\n return (\n <astro-dev-toolbar-button\n size=\"small\"\n style={{ padding: 0, margin: 0 }}\n buttonStyle={isSelected ? \"gray\" : \"ghost\"}\n className=\"zoom-button\"\n role=\"radio\"\n onClick={() => setImageSize(value)}\n >\n <ZoomLabel imageSize={value} />\n </astro-dev-toolbar-button>\n );\n}\n","import type { ComponentChildren } from \"preact\";\nimport type Preact from \"preact\";\n\nexport function ToolbarSection({\n icon,\n children,\n}: {\n icon: Preact.JSX.Element;\n children: ComponentChildren;\n}): Preact.JSX.Element {\n return (\n <div\n style={{\n fontSize: 12,\n display: \"flex\",\n alignItems: \"center\",\n columnGap: 5,\n }}\n >\n <div\n style={{\n display: \"inline-block\",\n width: 12,\n height: 12,\n }}\n >\n {icon}\n </div>\n {children}\n </div>\n );\n}\n","import type Preact from \"preact\";\nimport { ZoomButton } from \"./ZoomButton.js\";\nimport { ToolbarSection } from \"./ToolbarSection.js\";\n\nexport function ImageZoomToolbar({\n isHovered,\n imageSize,\n setImageSize,\n}: {\n isHovered: boolean;\n imageSize: number;\n setImageSize: (size: number) => void;\n}): Preact.JSX.Element {\n return (\n <ToolbarSection icon={<astro-dev-toolbar-icon icon=\"gear\" />}>\n {!isHovered ? (\n <ZoomButton\n value={imageSize}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n ) : (\n <>\n <ZoomButton\n value={25}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n <ZoomButton\n value={50}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n <ZoomButton\n value={75}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n <ZoomButton\n value={100}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n </>\n )}\n </ToolbarSection>\n );\n}\n","import { useState, useRef, useEffect, useCallback } from \"preact/hooks\";\nimport type Preact from \"preact\";\nimport type { MouseEventHandler } from \"preact/compat\";\nimport type { DevToolbarButton } from \"astro/runtime/client/dev-toolbar/ui-library/button.js\";\nimport { useImageURL } from \"../hooks/useImageURL.js\";\nimport { ToolbarSection } from \"./ToolbarSection.js\";\n\ntype Timer = ReturnType<typeof setTimeout>;\n\nexport function CopyImageUrlButton(): Preact.JSX.Element {\n const imageUrl = useImageURL();\n const [wasRecentlyClicked, setWasRecentlyClicked] = useState(false);\n const checkTimer = useRef<Timer | null>(null);\n\n useEffect(() => {\n if (wasRecentlyClicked && checkTimer.current == null) {\n checkTimer.current = setTimeout(() => {\n setWasRecentlyClicked(false);\n // TODO investigate\n // eslint-disable-next-line react-compiler/react-compiler\n checkTimer.current = null;\n }, 3_000);\n }\n }, [wasRecentlyClicked]);\n\n const handleClick: MouseEventHandler<DevToolbarButton> = useCallback(\n (evt) => {\n // Copy the text inside the text field\n void navigator.clipboard.writeText(imageUrl);\n setWasRecentlyClicked(true);\n evt.stopPropagation();\n evt.preventDefault();\n },\n [imageUrl],\n );\n\n return (\n <ToolbarSection icon={<astro-dev-toolbar-icon icon=\"copy\" />}>\n <div style={{ display: \"flex\", alignItems: \"center\" }}>\n <astro-dev-toolbar-button\n size=\"small\"\n buttonStyle=\"gray\"\n onClick={handleClick}\n >\n {wasRecentlyClicked ? \"Copied to clipboard\" : \"Copy image URL\"}\n </astro-dev-toolbar-button>\n </div>\n </ToolbarSection>\n );\n}\n","import { Image } from \"./Image.js\";\nimport { ImageZoomToolbar } from \"./ImageZoomToolbar.js\";\nimport { CopyImageUrlButton } from \"./CopyImageUrlButton.js\";\nimport type Preact from \"preact\";\nimport { useState } from \"preact/hooks\";\n\nexport function ImageSection({\n isHovered,\n handleLoadError,\n}: {\n isHovered: boolean;\n handleLoadError: () => void;\n}): Preact.JSX.Element {\n const [imageSize, setImageSize] = useState(100);\n\n return (\n <>\n <Image imageSize={imageSize} handleLoadError={handleLoadError} />\n <div\n data-testid=\"image-section-toolbar\"\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n columnGap: 15,\n }}\n >\n <CopyImageUrlButton />\n <ImageZoomToolbar\n isHovered={isHovered}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n </div>\n </>\n );\n}\n","import type { Icon } from \"astro/runtime/client/dev-toolbar/ui-library/icons.js\";\nimport type Preact from \"preact\";\n\nexport function Error({\n message,\n icon,\n}: {\n message: string;\n icon: Icon;\n}): Preact.JSX.Element {\n return (\n <div\n style={{\n display: \"flex\",\n gap: 10,\n alignItems: \"center\",\n }}\n >\n <astro-dev-toolbar-icon style={{ width: 24, height: 24 }} icon={icon} />{\" \"}\n {message}\n </div>\n );\n}\n","export function getRelativeUrl(absoluteUrl: string): string {\n const url = new URL(absoluteUrl);\n return url.pathname;\n}\n","import type Preact from \"preact\";\nimport { useState } from \"preact/hooks\";\nimport { Summary } from \"./Summary.js\";\nimport { MetaTags } from \"./MetaTags.js\";\nimport { useHoverState } from \"../hooks/useHoverState.js\";\nimport { useImageURLIfAvailable } from \"../hooks/useImageURL.js\";\nimport { ImageSection } from \"./ImageSection.js\";\nimport { Error } from \"./Error.js\";\nimport { getRelativeUrl } from \"./getRelativeUrl.js\";\n\nexport function ToolbarApp(): Preact.JSX.Element {\n const [isImageDetailsHovered, setIsImageDetailsHovered] = useState(false);\n const imageDetailsRef = useHoverState<HTMLDetailsElement>(\n setIsImageDetailsHovered,\n );\n const imageUrl = useImageURLIfAvailable();\n const [imageLoadStatus, setImageLoadStatus] = useState<\"pending\" | \"error\">(\n \"pending\",\n );\n\n if (imageUrl == null) {\n return (\n <Error icon=\"warning\" message=\"No Open Graph image found on this page\" />\n );\n } else if (imageLoadStatus === \"error\") {\n return (\n <Error\n icon=\"bug\"\n message={`Error loading image at ${getRelativeUrl(imageUrl)}`}\n />\n );\n }\n\n return (\n <>\n <details open className=\"image\" ref={imageDetailsRef}>\n <Summary>Image</Summary>\n <ImageSection\n isHovered={isImageDetailsHovered}\n handleLoadError={() => setImageLoadStatus(\"error\")}\n />\n </details>\n\n <details>\n <Summary>Meta Tags</Summary>\n <MetaTags />\n </details>\n </>\n );\n}\n","import type Preact from \"preact\";\nimport { styles } from \"../styles.js\";\nimport { ToolbarApp } from \"./ToolbarApp.js\";\n\nexport function ToolbarAppWindow(): Preact.JSX.Element {\n return (\n <astro-dev-toolbar-window>\n <style>{styles}</style>\n <ToolbarApp />\n </astro-dev-toolbar-window>\n );\n}\n","import { render } from \"preact\";\n\nimport { defineToolbarApp } from \"astro/toolbar\";\nimport { ToolbarAppWindow } from \"./components/ToolbarAppWindow.js\";\nimport type { DevToolbarApp } from \"astro\";\n\nconst SESSION_KEY_OPEN = \"@altano/astro-opengraph/toolbar:is-open\";\n\nconst app: DevToolbarApp = defineToolbarApp({\n init(canvas, app) {\n app.onToggled((event) => {\n if (event.state) {\n sessionStorage.setItem(SESSION_KEY_OPEN, \"true\");\n render(<ToolbarAppWindow />, canvas);\n } else {\n sessionStorage.removeItem(SESSION_KEY_OPEN);\n }\n });\n\n app.toggleNotification({\n state: document.querySelector('meta[property=\"og:image\"]') != null,\n level: \"info\",\n });\n\n if (sessionStorage.getItem(SESSION_KEY_OPEN) != null) {\n app.toggleState({ state: true });\n }\n },\n});\n\nexport default app;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["Error","Error"],"sources":["../../src/toolbar/styles.tsx","../../src/toolbar/components/Summary.tsx","../../src/toolbar/components/ExternalLinkicon.tsx","../../src/toolbar/components/LinkOrText.tsx","../../src/toolbar/hooks/useMetaTagInfo.tsx","../../src/toolbar/components/MetaTags.tsx","../../src/toolbar/hooks/useHoverState.tsx","../../src/toolbar/hooks/useImageURL.tsx","../../src/toolbar/components/Image.tsx","../../src/toolbar/components/ZoomLabel.tsx","../../src/toolbar/components/ZoomButton.tsx","../../src/toolbar/components/ToolbarSection.tsx","../../src/toolbar/components/ImageZoomToolbar.tsx","../../src/toolbar/components/CopyImageUrlButton.tsx","../../src/toolbar/components/ImageSection.tsx","../../src/toolbar/components/Error.tsx","../../src/toolbar/components/getRelativeUrl.tsx","../../src/toolbar/components/ToolbarApp.tsx","../../src/toolbar/components/ToolbarAppWindow.tsx","../../src/toolbar/index.tsx"],"sourcesContent":["export const styles = /* css */ `\n :host astro-dev-toolbar-window {\n max-height: calc(100vh - 80px);\n overflow-y: auto;\n }\n\n button {\n background: none;\n color: inherit;\n border: none;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n\n details {\n margin-block-end: 10px;\n }\n details:last-of-type {\n margin-block-end: 0;\n }\n\n details[open] summary, .image {\n margin-block-end: 7px;\n }\n\n .image img {\n transition: max-width 0.1s;\n }\n`;\n","import type Preact from \"preact\";\n\nexport function Summary({\n children,\n}: {\n children: string;\n}): Preact.JSX.Element {\n return (\n <summary\n style={{\n cursor: \"pointer\",\n fontWeight: 700,\n color: \"white\",\n }}\n >\n {children}\n </summary>\n );\n}\n","import type Preact from \"preact\";\n\nexport function ExternalLinkicon({\n width = 27,\n height = 27,\n fill = \"#020202\",\n}: {\n width?: number;\n height?: number;\n fill?: string;\n}): Preact.JSX.Element {\n return (\n <span\n style={{\n display: \"inline-block\",\n width,\n height,\n }}\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 120 120\">\n <path d=\"M120.09 16.696H60.256V.096L120.09.09z\" fill={fill} />\n <path\n d=\"M57.99 48.64l42.573-42.574 13.475 13.475-42.574 42.58z\"\n fill={fill}\n />\n <path\n d=\"M119.98.107l.02 59.83-16.59.013-.02-59.846zM3 23.5h17v87H3zm83.49 52.56h17V113h-17z\"\n fill={fill}\n />\n <path d=\"M3 16.692h40.655v17H3zM3 96h100.49v17H3z\" fill={fill} />\n </svg>\n </span>\n );\n}\n","import type Preact from \"preact\";\nimport { ExternalLinkicon } from \"./ExternalLinkicon.js\";\n\ntype LinkOrTextType = Preact.JSX.Element | string | null;\n\nexport function LinkOrText({\n maybeLinkText,\n}: {\n maybeLinkText: string | null;\n}): LinkOrTextType {\n if (maybeLinkText == null) {\n return null;\n }\n // Only the parse goes in the try. Constructing JSX inside a try/catch doesn't\n // do what it looks like it does -- React renders the element later, so render\n // errors escape the catch (react-hooks/error-boundaries).\n let url: URL;\n try {\n url = new URL(maybeLinkText);\n } catch (_) {\n return maybeLinkText;\n }\n return (\n <a href={url.toString()} target=\"_blank\">\n {maybeLinkText} <ExternalLinkicon width={10} height={10} fill=\"white\" />\n </a>\n );\n}\n","import { useEffect, useMemo, useRef, useState } from \"preact/hooks\";\n\nexport type MetaTagInfo = [content: string | null, property: string | null];\n\nconst useHeadObserver = (): React.RefObject<HTMLHeadElement | null> => {\n const [_generation, setGeneration] = useState<number>(0);\n const forceRerender = (): void => setGeneration((old) => ++old);\n const headRef = useRef(document.querySelector(\"head\"));\n\n // useEffect(() => {\n // console.log({ generation });\n // }, [generation]);\n\n useEffect(() => {\n // console.log({ \"headRef.current\": headRef.current });\n if (headRef.current == null) {\n // assume we must have a head by now, and if we don't, we will never get\n // one.\n return;\n }\n const observer = new MutationObserver(forceRerender);\n observer.observe(headRef.current, {\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n });\n return () => observer.disconnect();\n }, []);\n\n return headRef;\n};\n\nexport function useMetaTagInfo(): MetaTagInfo[] {\n const headRef = useHeadObserver();\n // TODO: This works because the MutationObserver above forces a re-render on\n // every <head> change, but the supported shape for this is\n // useSyncExternalStore.\n const opengraphMetaTags = useMemo(() => {\n return headRef?.current?.querySelectorAll(\"meta[property^='og:']\") ?? [];\n }, [headRef]);\n\n // console.log({\n // opengraphMetaTags: opengraphMetaTags,\n // opengraphMetaTagsLength: opengraphMetaTags.length,\n // });\n return Array.from(opengraphMetaTags).map((tag: Element): MetaTagInfo => [\n tag.getAttribute(\"content\"),\n tag.getAttribute(\"property\"),\n ]);\n}\n","import type Preact from \"preact\";\nimport { LinkOrText } from \"./LinkOrText.js\";\nimport { useMetaTagInfo, type MetaTagInfo } from \"../hooks/useMetaTagInfo.js\";\n\nexport function MetaTags(): Preact.JSX.Element {\n const tags = useMetaTagInfo();\n\n return (\n <dl\n data-testid=\"astro-opengraph-toolbar\"\n style={{\n margin: 0,\n display: \"grid\",\n gridTemplateColumns: \"max-content 1fr\",\n gap: 7,\n columnGap: 13,\n }}\n >\n {tags.map((tag) => (\n <MetaTagContent key={tag[1]} tag={tag} />\n ))}\n </dl>\n );\n}\n\nfunction MetaTagContent({ tag }: { tag: MetaTagInfo }): Preact.JSX.Element {\n const [content, property] = tag;\n return (\n <>\n <dt\n style={{\n color: \"white\",\n textAlign: \"right\",\n fontWeight: 500,\n }}\n >\n {property}\n </dt>\n <dd style={{ margin: 0 }}>\n <LinkOrText maybeLinkText={content} />\n </dd>\n </>\n );\n}\n","import type Preact from \"preact\";\nimport { useRef, useEffect } from \"preact/hooks\";\n\nexport function useHoverState<TElement extends HTMLElement>(\n setHoverState: (isHover: boolean) => void,\n): Preact.RefObject<TElement> {\n const ref = useRef<TElement>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n const { signal } = controller;\n ref.current?.addEventListener(\"mouseenter\", () => setHoverState(true), {\n signal,\n });\n ref.current?.addEventListener(\"mouseleave\", () => setHoverState(false), {\n signal,\n });\n\n return () => controller.abort();\n }, [setHoverState]);\n\n return ref;\n}\n","import { useState } from \"preact/hooks\";\n\nexport function useImageURL(): string {\n const imageUrl = useImageURLIfAvailable();\n if (imageUrl == null) {\n throw new Error(\n `Shouldn't be rendering this component without an image URL`,\n );\n }\n return imageUrl;\n}\n\nexport function useImageURLIfAvailable(): string | null {\n const [imageUrlRef] = useState(() => {\n const meta = document.querySelector('meta[property=\"og:image\"]');\n return meta?.getAttribute(\"content\") ?? null;\n });\n return imageUrlRef;\n}\n","import type Preact from \"preact\";\nimport { useImageURL } from \"../hooks/useImageURL.js\";\n\nexport function Image({\n imageSize,\n handleLoadError,\n}: {\n imageSize: number;\n handleLoadError: () => void;\n}): Preact.JSX.Element {\n const imageUrl = useImageURL();\n return (\n <div className=\"image\">\n <a href={imageUrl} target=\"_blank\" title=\"Open image in new tab\">\n <img\n onError={handleLoadError}\n alt=\"Open Graph\"\n src={imageUrl}\n style={{\n maxHeight: \"100%\",\n maxWidth: `${imageSize}%`,\n }}\n />\n </a>\n </div>\n );\n}\n","export function ZoomLabel({ imageSize }: { imageSize: number }): string {\n const formattedNumber = (imageSize / 100).toLocaleString(undefined, {\n minimumFractionDigits: 2,\n });\n return `${formattedNumber}x`;\n}\n","import type Preact from \"preact\";\nimport { ZoomLabel } from \"./ZoomLabel.js\";\n\nexport function ZoomButton({\n value,\n imageSize,\n setImageSize,\n}: {\n value: number;\n imageSize: number;\n setImageSize: (size: number) => void;\n}): Preact.JSX.Element {\n const isSelected = value === imageSize;\n return (\n <astro-dev-toolbar-button\n size=\"small\"\n style={{ padding: 0, margin: 0 }}\n buttonStyle={isSelected ? \"gray\" : \"ghost\"}\n className=\"zoom-button\"\n role=\"radio\"\n onClick={() => setImageSize(value)}\n >\n <ZoomLabel imageSize={value} />\n </astro-dev-toolbar-button>\n );\n}\n","import type { ComponentChildren } from \"preact\";\nimport type Preact from \"preact\";\n\nexport function ToolbarSection({\n icon,\n children,\n}: {\n icon: Preact.JSX.Element;\n children: ComponentChildren;\n}): Preact.JSX.Element {\n return (\n <div\n style={{\n fontSize: 12,\n display: \"flex\",\n alignItems: \"center\",\n columnGap: 5,\n }}\n >\n <div\n style={{\n display: \"inline-block\",\n width: 12,\n height: 12,\n }}\n >\n {icon}\n </div>\n {children}\n </div>\n );\n}\n","import type Preact from \"preact\";\nimport { ZoomButton } from \"./ZoomButton.js\";\nimport { ToolbarSection } from \"./ToolbarSection.js\";\n\nexport function ImageZoomToolbar({\n isHovered,\n imageSize,\n setImageSize,\n}: {\n isHovered: boolean;\n imageSize: number;\n setImageSize: (size: number) => void;\n}): Preact.JSX.Element {\n return (\n <ToolbarSection icon={<astro-dev-toolbar-icon icon=\"gear\" />}>\n {!isHovered ? (\n <ZoomButton\n value={imageSize}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n ) : (\n <>\n <ZoomButton\n value={25}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n <ZoomButton\n value={50}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n <ZoomButton\n value={75}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n <ZoomButton\n value={100}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n </>\n )}\n </ToolbarSection>\n );\n}\n","import { useState, useEffect, useCallback } from \"preact/hooks\";\nimport type Preact from \"preact\";\nimport type { DevToolbarButton } from \"astro/runtime/client/dev-toolbar/ui-library/button.js\";\nimport { useImageURL } from \"../hooks/useImageURL.js\";\nimport { ToolbarSection } from \"./ToolbarSection.js\";\n\nexport function CopyImageUrlButton(): Preact.JSX.Element {\n const imageUrl = useImageURL();\n const [wasRecentlyClicked, setWasRecentlyClicked] = useState(false);\n\n useEffect(() => {\n if (!wasRecentlyClicked) return;\n const timer = setTimeout(() => setWasRecentlyClicked(false), 3_000);\n return () => clearTimeout(timer);\n }, [wasRecentlyClicked]);\n\n const handleClick: Preact.MouseEventHandler<DevToolbarButton> = useCallback(\n (evt) => {\n // Copy the text inside the text field\n void navigator.clipboard.writeText(imageUrl);\n setWasRecentlyClicked(true);\n evt.stopPropagation();\n evt.preventDefault();\n },\n [imageUrl],\n );\n\n return (\n <ToolbarSection icon={<astro-dev-toolbar-icon icon=\"copy\" />}>\n <div style={{ display: \"flex\", alignItems: \"center\" }}>\n <astro-dev-toolbar-button\n size=\"small\"\n buttonStyle=\"gray\"\n onClick={handleClick}\n >\n {wasRecentlyClicked ? \"Copied to clipboard\" : \"Copy image URL\"}\n </astro-dev-toolbar-button>\n </div>\n </ToolbarSection>\n );\n}\n","import { Image } from \"./Image.js\";\nimport { ImageZoomToolbar } from \"./ImageZoomToolbar.js\";\nimport { CopyImageUrlButton } from \"./CopyImageUrlButton.js\";\nimport type Preact from \"preact\";\nimport { useState } from \"preact/hooks\";\n\nexport function ImageSection({\n isHovered,\n handleLoadError,\n}: {\n isHovered: boolean;\n handleLoadError: () => void;\n}): Preact.JSX.Element {\n const [imageSize, setImageSize] = useState(100);\n\n return (\n <>\n <Image imageSize={imageSize} handleLoadError={handleLoadError} />\n <div\n data-testid=\"image-section-toolbar\"\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n columnGap: 15,\n }}\n >\n <CopyImageUrlButton />\n <ImageZoomToolbar\n isHovered={isHovered}\n imageSize={imageSize}\n setImageSize={setImageSize}\n />\n </div>\n </>\n );\n}\n","import type { Icon } from \"astro/runtime/client/dev-toolbar/ui-library/icons.js\";\nimport type Preact from \"preact\";\n\nexport function Error({\n message,\n icon,\n}: {\n message: string;\n icon: Icon;\n}): Preact.JSX.Element {\n return (\n <div\n style={{\n display: \"flex\",\n gap: 10,\n alignItems: \"center\",\n }}\n >\n <astro-dev-toolbar-icon style={{ width: 24, height: 24 }} icon={icon} />{\" \"}\n {message}\n </div>\n );\n}\n","export function getRelativeUrl(absoluteUrl: string): string {\n const url = new URL(absoluteUrl);\n return url.pathname;\n}\n","import type Preact from \"preact\";\nimport { useState } from \"preact/hooks\";\nimport { Summary } from \"./Summary.js\";\nimport { MetaTags } from \"./MetaTags.js\";\nimport { useHoverState } from \"../hooks/useHoverState.js\";\nimport { useImageURLIfAvailable } from \"../hooks/useImageURL.js\";\nimport { ImageSection } from \"./ImageSection.js\";\nimport { Error } from \"./Error.js\";\nimport { getRelativeUrl } from \"./getRelativeUrl.js\";\n\nexport function ToolbarApp(): Preact.JSX.Element {\n const [isImageDetailsHovered, setIsImageDetailsHovered] = useState(false);\n const imageDetailsRef = useHoverState<HTMLDetailsElement>(\n setIsImageDetailsHovered,\n );\n const imageUrl = useImageURLIfAvailable();\n const [imageLoadStatus, setImageLoadStatus] = useState<\"pending\" | \"error\">(\n \"pending\",\n );\n\n if (imageUrl == null) {\n return (\n <Error icon=\"warning\" message=\"No Open Graph image found on this page\" />\n );\n } else if (imageLoadStatus === \"error\") {\n return (\n <Error\n icon=\"bug\"\n message={`Error loading image at ${getRelativeUrl(imageUrl)}`}\n />\n );\n }\n\n return (\n <>\n <details open className=\"image\" ref={imageDetailsRef}>\n <Summary>Image</Summary>\n <ImageSection\n isHovered={isImageDetailsHovered}\n handleLoadError={() => setImageLoadStatus(\"error\")}\n />\n </details>\n\n <details>\n <Summary>Meta Tags</Summary>\n <MetaTags />\n </details>\n </>\n );\n}\n","import type Preact from \"preact\";\nimport { styles } from \"../styles.js\";\nimport { ToolbarApp } from \"./ToolbarApp.js\";\n\nexport function ToolbarAppWindow(): Preact.JSX.Element {\n return (\n <astro-dev-toolbar-window>\n <style>{styles}</style>\n <ToolbarApp />\n </astro-dev-toolbar-window>\n );\n}\n","import { render } from \"preact\";\n\nimport { defineToolbarApp } from \"astro/toolbar\";\nimport { ToolbarAppWindow } from \"./components/ToolbarAppWindow.js\";\nimport type { DevToolbarApp } from \"astro\";\n\nconst SESSION_KEY_OPEN = \"@altano/astro-opengraph/toolbar:is-open\";\n\nconst app: DevToolbarApp = defineToolbarApp({\n init(canvas, app) {\n app.onToggled((event) => {\n if (event.state) {\n sessionStorage.setItem(SESSION_KEY_OPEN, \"true\");\n render(<ToolbarAppWindow />, canvas);\n } else {\n sessionStorage.removeItem(SESSION_KEY_OPEN);\n }\n });\n\n app.toggleNotification({\n state: document.querySelector('meta[property=\"og:image\"]') != null,\n level: \"info\",\n });\n\n if (sessionStorage.getItem(SESSION_KEY_OPEN) != null) {\n app.toggleState({ state: true });\n }\n },\n});\n\nexport default app;\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { SatoriOptions } from "satori";
2
-
3
2
  //#region src/types.d.ts
4
3
  type ImageOptions = SatoriOptions;
5
4
  type FontWithBuffer = ImageOptions["fonts"][0];
@@ -35,20 +34,20 @@ type MetaTagDefaults = {
35
34
  };
36
35
  type OpengraphImageConfig = {
37
36
  /**
38
- * Options to use for the image file that is generated. Most importantly, fonts
39
- * must be provided.
40
- */
37
+ * Options to use for the image file that is generated. Most importantly, fonts
38
+ * must be provided.
39
+ */
41
40
  getImageOptions(): Promise<ImageOptionsWithFontPaths>;
42
41
  /**
43
- * Defaults to use in the component for tags you don't manually set.
44
- */
42
+ * Defaults to use in the component for tags you don't manually set.
43
+ */
45
44
  componentMetaTagFallbacks?: ComponentMetaTagFallbacks;
46
45
  };
47
46
  type AstroConfigSetupHookOptions = Parameters<Astro.IntegrationHooks["astro:config:setup"]>[0];
48
47
  type Command = AstroConfigSetupHookOptions["command"];
49
48
  /**
50
- * This must remain JSON-serializable!
51
- */
49
+ * This must remain JSON-serializable!
50
+ */
52
51
  type OpengraphImageConfigSerializable = {
53
52
  imageOptions: ImageOptionsWithFontPaths;
54
53
  componentMetaTagFallbacks?: ComponentMetaTagFallbacks;
@@ -67,4 +66,4 @@ type OpengraphImageConfigResolved = {
67
66
  };
68
67
  //#endregion
69
68
  export { ComponentMetaTagFallbacks, ImageOptions, ImageOptionsWithFontPaths, OpengraphImageConfig, OpengraphImageConfigDeserialized, OpengraphImageConfigResolved, OpengraphImageConfigSerializableMaybeMocked };
70
- //# sourceMappingURL=types-BGbFKpbi.d.ts.map
69
+ //# sourceMappingURL=types-DCSh1ad6.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@altano/astro-opengraph",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Open Graph Tools for Astro",
5
5
  "keywords": [
6
6
  "astro",
@@ -20,7 +20,7 @@
20
20
  "license": "ISC",
21
21
  "author": {
22
22
  "name": "Alan Norbauer",
23
- "email": "altano@gmail.com",
23
+ "email": "alan@norbauer.com",
24
24
  "url": "https://alan.norbauer.com"
25
25
  },
26
26
  "sideEffects": false,
@@ -41,55 +41,57 @@
41
41
  ],
42
42
  "dependencies": {
43
43
  "he": "^1.2.0",
44
- "satori": "^0.12.2",
44
+ "satori": "^0.29.0",
45
45
  "satori-html": "^0.3.2"
46
46
  },
47
47
  "devDependencies": {
48
- "@astrojs/check": "^0.9.8",
49
- "@astrojs/preact": "^5.0.2",
50
- "@astrojs/svelte": "^8.0.3",
51
- "@astrojs/vue": "^6.0.1",
52
- "@inox-tools/astro-tests": "^0.9.1",
53
- "@playwright/test": "1.58.2",
48
+ "@astrojs/check": "^0.9.9",
49
+ "@astrojs/preact": "^6.0.1",
50
+ "@astrojs/svelte": "^9.0.1",
51
+ "@astrojs/vue": "^7.0.1",
52
+ "@inox-tools/astro-tests": "^2.0.0",
53
+ "@playwright/test": "1.59.1",
54
54
  "@types/he": "^1.2.3",
55
55
  "@types/jest-image-snapshot": "^6.4.1",
56
- "@types/node": "^25.5.0",
57
- "@vitest/coverage-v8": "^4.1.0",
58
- "astro": "^6.0.8",
56
+ "@types/node": "^26.1.1",
57
+ "@vitest/coverage-v8": "^4.1.10",
58
+ "astro": "^7.1.3",
59
59
  "depcheck": "^1.4.7",
60
- "jest-image-snapshot": "^6.5.1",
61
- "preact": "^10.28.2",
62
- "sharp": "^0.33.5",
63
- "svelte": "^5.53.5",
64
- "tsdown": "^0.21.4",
65
- "typescript": "^5.9.3",
66
- "vite": "^7.3.1",
67
- "vitest": "^4.1.0",
68
- "vue": "^3.5.27",
60
+ "jest-image-snapshot": "^6.5.2",
61
+ "preact": "^10.29.7",
62
+ "prettier": "3.9.6",
63
+ "sharp": "^0.35.3",
64
+ "svelte": "^5.56.7",
65
+ "tsdown": "^0.22.13",
66
+ "typescript": "^6.0.3",
67
+ "vite": "^8.1.5",
68
+ "vitest": "^4.1.10",
69
+ "vue": "^3.5.40",
69
70
  "zx": "^8.8.5",
70
71
  "@altano/assets": "0.0.1",
71
72
  "@altano/build-config": "0.0.1",
72
73
  "@altano/testing": "0.0.1",
73
- "@altano/tiny-async-pool": "4.0.2",
74
+ "@altano/tiny-async-pool": "4.0.3",
74
75
  "@altano/tsconfig": "0.0.1",
75
- "@altano/vitest-plugins": "1.1.2"
76
+ "@altano/vitest-plugins": "1.1.3"
76
77
  },
77
78
  "peerDependencies": {
78
- "astro": "6.x.x",
79
+ "astro": "7.x.x",
79
80
  "preact": ">=10"
80
81
  },
81
82
  "publishConfig": {
82
83
  "access": "public"
83
84
  },
84
85
  "scripts": {
85
- "build": "tsdown --config-loader unrun",
86
+ "build": "tsdown",
86
87
  "check:types:astro": "node --experimental-strip-types ./scripts/check-fixtures.ts",
87
88
  "check:types:src": "tsc --noEmit",
88
89
  "check:types:test": "tsc --noEmit --project ./tests/tsconfig.json",
89
90
  "check:unused-dependencies": "depcheck",
90
91
  "clean": "rm -rf .turbo && rm -rf .tsbuildinfo && rm -rf node_modules && rm -rf dist",
91
92
  "dev": "tsdown --watch",
92
- "format": "prettier --write src",
93
+ "format": "prettier --check .",
94
+ "format:fix": "prettier --write .",
93
95
  "lint": "eslint",
94
96
  "lint:fix": "TIMING=1 eslint src/**/*.ts* --fix",
95
97
  "lint:timing": "TIMING=1 eslint src/**/*.ts*",