@vercel/og 0.5.20 → 0.6.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.
@@ -0,0 +1,2 @@
1
+ import { ImageResponse } from 'src/index.edge';
2
+ export declare type EdgeImageResponse = typeof ImageResponse;
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import type { ReactElement } from 'react';
3
- import type { ImageResponseNodeOptions, ImageResponseOptions } from './types';
3
+ import type { ImageResponseNodeOptions, ImageResponseOptions, FigmaImageResponseProps } from './types';
4
4
  import { Readable } from 'stream';
5
5
  export declare class ImageResponse extends Response {
6
6
  constructor(element: ReactElement, options?: ImageResponseOptions);
@@ -23,3 +23,4 @@ export declare class ImageResponse extends Response {
23
23
  * ```
24
24
  */
25
25
  export declare function unstable_createNodejsStream(element: ReactElement, options?: Omit<ImageResponseNodeOptions, 'status' | 'statusText' | 'headers'>): Promise<Readable>;
26
+ export declare const experimental_FigmaImageResponse: (props: FigmaImageResponseProps) => Promise<import("./index.edge").ImageResponse>;
@@ -18845,6 +18845,196 @@ async function render(satori2, resvg, opts, defaultFonts, element) {
18845
18845
  return resvgJS.render().asPng();
18846
18846
  }
18847
18847
 
18848
+ // src/figma/index.tsx
18849
+ var FigmaImageResponse = async ({
18850
+ url,
18851
+ template,
18852
+ fonts,
18853
+ imageResponseOptions,
18854
+ Response: Response2
18855
+ }) => {
18856
+ const { fileId, nodeId } = parseFigmaUrl(url);
18857
+ const figmaAPIToken = assertValue(process.env.FIGMA_PERSONAL_ACCESS_TOKEN, "Missing environment variable: `FIGMA_PERSONAL_ACCESS_TOKEN`. You can get one at https://www.figma.com/developers/api#authentication");
18858
+ const figmaResponse = await fetch(`https://api.figma.com/v1/images/${fileId}?ids=${nodeId}&svg_outline_text=false&format=svg&svg_include_id=true`, {
18859
+ method: "GET",
18860
+ headers: {
18861
+ "X-FIGMA-TOKEN": figmaAPIToken
18862
+ },
18863
+ cache: "no-store"
18864
+ });
18865
+ const figmaResponseJson = await figmaResponse.json();
18866
+ const svgDownloadPath = figmaResponseJson.images[nodeId.replace("-", ":")];
18867
+ const svgResponse = await fetch(svgDownloadPath, { cache: "no-store" });
18868
+ const svg = await svgResponse.text();
18869
+ const { width, height } = getSvgDimensions(svg);
18870
+ const textNodes = getTextNodes(svg);
18871
+ const textNodeAttributes = textNodes.map(parseSvgText);
18872
+ return new Response2({
18873
+ key: "0",
18874
+ type: "div",
18875
+ props: {
18876
+ style: { display: "flex" },
18877
+ children: [
18878
+ {
18879
+ type: "img",
18880
+ props: {
18881
+ style: { position: "absolute" },
18882
+ alt: "",
18883
+ width,
18884
+ height,
18885
+ src: svgToBase64(svg)
18886
+ }
18887
+ },
18888
+ {
18889
+ type: "div",
18890
+ props: {
18891
+ style: { display: "flex", position: "relative", width: "100%" },
18892
+ children: textNodeAttributes.map((textNode) => {
18893
+ const t = template[textNode.id];
18894
+ let value = "";
18895
+ if (t === void 0) {
18896
+ value = textNode.content;
18897
+ } else if (isComplexTemplate(t)) {
18898
+ value = t.value;
18899
+ } else {
18900
+ value = t;
18901
+ }
18902
+ let cssProps = {};
18903
+ let centerHorizontally = false;
18904
+ if (isComplexTemplate(t) && t.props) {
18905
+ const {
18906
+ centerHorizontally: centerHorizontallyProp,
18907
+ ...otherCSSProps
18908
+ } = t.props;
18909
+ cssProps = otherCSSProps;
18910
+ centerHorizontally = centerHorizontallyProp || false;
18911
+ }
18912
+ if (centerHorizontally) {
18913
+ const templateStyles = {
18914
+ color: textNode.fill,
18915
+ marginTop: `${parseInt(textNode.y) - parseInt(textNode.fontSize)}px`,
18916
+ fontWeight: textNode.fontWeight || "400",
18917
+ fontSize: textNode.fontSize,
18918
+ fontFamily: textNode.fontFamily,
18919
+ letterSpacing: textNode.letterSpacing,
18920
+ textAlign: "center",
18921
+ ...cssProps
18922
+ };
18923
+ return {
18924
+ type: "div",
18925
+ props: {
18926
+ style: {
18927
+ display: "flex",
18928
+ justifyContent: "center",
18929
+ position: "absolute",
18930
+ width: "100%"
18931
+ },
18932
+ children: {
18933
+ type: "div",
18934
+ props: {
18935
+ style: templateStyles,
18936
+ children: value
18937
+ }
18938
+ }
18939
+ }
18940
+ };
18941
+ }
18942
+ return {
18943
+ type: "div",
18944
+ props: {
18945
+ style: {
18946
+ position: "absolute",
18947
+ color: textNode.fill,
18948
+ left: `${textNode.x}px`,
18949
+ top: `${parseInt(textNode.y) - parseInt(textNode.fontSize)}px`,
18950
+ fontWeight: textNode.fontWeight || "400",
18951
+ fontSize: textNode.fontSize,
18952
+ fontFamily: textNode.fontFamily,
18953
+ letterSpacing: textNode.letterSpacing,
18954
+ ...cssProps
18955
+ },
18956
+ children: value
18957
+ }
18958
+ };
18959
+ })
18960
+ }
18961
+ }
18962
+ ]
18963
+ }
18964
+ }, {
18965
+ width,
18966
+ height,
18967
+ fonts,
18968
+ ...imageResponseOptions
18969
+ });
18970
+ };
18971
+ var isComplexTemplate = (template) => {
18972
+ return typeof template !== "string" && template !== void 0 && "value" in template;
18973
+ };
18974
+ function svgToBase64(svg) {
18975
+ const base64 = Buffer.from(svg).toString("base64");
18976
+ return "data:image/svg+xml;base64," + base64;
18977
+ }
18978
+ function getSvgDimensions(svg) {
18979
+ const widthMatch = svg.match(/width="(\d+)/);
18980
+ const heightMatch = svg.match(/height="(\d+)/);
18981
+ if (widthMatch && heightMatch) {
18982
+ const width = parseInt(widthMatch[1], 10);
18983
+ const height = parseInt(heightMatch[1], 10);
18984
+ return { width, height };
18985
+ }
18986
+ return { width: 0, height: 0 };
18987
+ }
18988
+ function getTextNodes(svg) {
18989
+ const regex = /<text[^>]*>(.*?)<\/text>/g;
18990
+ let match;
18991
+ const matches = [];
18992
+ while ((match = regex.exec(svg)) !== null) {
18993
+ matches.push(match[0]);
18994
+ }
18995
+ return matches;
18996
+ }
18997
+ function parseSvgText(svgText) {
18998
+ var _a2, _b2, _c, _d, _e, _f2, _g, _h, _i;
18999
+ const id = ((_a2 = svgText.match(/id="([^"]*)"/)) == null ? void 0 : _a2[1]) || "";
19000
+ const fill = ((_b2 = svgText.match(/fill="([^"]*)"/)) == null ? void 0 : _b2[1]) || "";
19001
+ const fontFamily = ((_c = svgText.match(/font-family="([^"]*)"/)) == null ? void 0 : _c[1]) || "";
19002
+ const fontSize = ((_d = svgText.match(/font-size="([^"]*)"/)) == null ? void 0 : _d[1]) || "";
19003
+ const fontWeight = ((_e = svgText.match(/font-weight="([^"]*)"/)) == null ? void 0 : _e[1]) || "";
19004
+ const letterSpacing = ((_f2 = svgText.match(/letter-spacing="([^"]*)"/)) == null ? void 0 : _f2[1]) || "";
19005
+ const x = ((_g = svgText.match(/<tspan[^>]*x="([^"]*)"/)) == null ? void 0 : _g[1]) || "";
19006
+ const y = ((_h = svgText.match(/<tspan[^>]*y="([^"]*)"/)) == null ? void 0 : _h[1]) || "";
19007
+ const content = ((_i = svgText.match(/<tspan[^>]*>([^<]*)<\/tspan>/)) == null ? void 0 : _i[1]) || "";
19008
+ return {
19009
+ id,
19010
+ fill,
19011
+ fontFamily,
19012
+ fontSize,
19013
+ fontWeight,
19014
+ letterSpacing,
19015
+ x,
19016
+ y,
19017
+ content
19018
+ };
19019
+ }
19020
+ function parseFigmaUrl(figmaUrl) {
19021
+ const regex = /\/file\/([^/]+)\/[^?]+\?[^#]*node-id=([^&#]+)/;
19022
+ const match = figmaUrl.match(regex);
19023
+ let fileId = "";
19024
+ let nodeId = "";
19025
+ if (match) {
19026
+ fileId = match[1] || "";
19027
+ nodeId = match[2] || "";
19028
+ }
19029
+ return { fileId, nodeId };
19030
+ }
19031
+ function assertValue(v, errorMessage) {
19032
+ if (v === void 0) {
19033
+ throw new Error(errorMessage);
19034
+ }
19035
+ return v;
19036
+ }
19037
+
18848
19038
  // src/index.node.ts
18849
19039
  var satori = wl.default || wl;
18850
19040
  var fontData = fs.readFileSync(fileURLToPath(join(import.meta.url, "../noto-sans-v27-latin-regular.ttf")));
@@ -18899,8 +19089,12 @@ async function unstable_createNodejsStream(element, options = {}) {
18899
19089
  const result = await render(satori, resvg_wasm_exports, options, fonts, element);
18900
19090
  return Readable.from(Buffer.from(result));
18901
19091
  }
19092
+ var experimental_FigmaImageResponse = async (props) => {
19093
+ return FigmaImageResponse({ ...props, Response: ImageResponse });
19094
+ };
18902
19095
  export {
18903
19096
  ImageResponse,
19097
+ experimental_FigmaImageResponse,
18904
19098
  unstable_createNodejsStream
18905
19099
  };
18906
19100
  /*!