From 4f1277ef8190d5bbb7910a82f42cae534525e45e Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 11:26:28 +0900 Subject: [PATCH 01/11] mv --- code/README.md | 1 + code/index.ts | 98 +++++++++++++++++++++++++++++ code/package.json | 4 ++ editor/next.config.js | 1 + editor/pages/api/v1/code/index.ts | 100 +----------------------------- package.json | 69 +++++++++++---------- 6 files changed, 140 insertions(+), 133 deletions(-) create mode 100644 code/README.md create mode 100644 code/index.ts create mode 100644 code/package.json diff --git a/code/README.md b/code/README.md new file mode 100644 index 000000000..b9cb3660c --- /dev/null +++ b/code/README.md @@ -0,0 +1 @@ +# Grida API Core - This is not a Client Library diff --git a/code/index.ts b/code/index.ts new file mode 100644 index 000000000..91d17e06c --- /dev/null +++ b/code/index.ts @@ -0,0 +1,98 @@ +import { designToCode, Result } from "@designto/code"; +import { DesignInput } from "@grida/builder-config/input"; +import { parseFileAndNodeId } from "@design-sdk/figma-url"; +import { fetchTargetAsReflect } from "@design-sdk/figma-remote"; +import { + ImageRepository, + MainImageRepository, +} from "@design-sdk/asset-repository"; +import { RemoteImageRepositories } from "@design-sdk/figma-remote/asset-repository"; +import type { FrameworkConfig } from "@grida/builder-config"; +import { defaultConfigByFramework } from "@grida/builder-config-preset"; +import { Language } from "@grida/builder-platform-types"; +import { formatCode } from "dart-style"; + +export async function code({ + auth, + uri, + framework, +}: { + auth: + | { + personalAccessToken: string; + } + | { accessToken: string }; + uri: string; + framework: FrameworkConfig; +}) { + // + + const res = parseFileAndNodeId(uri as string); + if (res) { + const { file: filekey, node } = res; + + console.log(`filekey: ${filekey}`, `node: ${node}`); + + // + const target = await fetchTargetAsReflect({ + file: filekey, + node, + auth: auth, + }); + + MainImageRepository.instance = new RemoteImageRepositories(target.file, { + authentication: auth, + }); + MainImageRepository.instance.register( + new ImageRepository( + "fill-later-assets", + "grida://assets-reservation/images/" + ) + ); + + const code = await designToCode({ + input: DesignInput.fromApiResponse({ + raw: target.raw, + entry: target.reflect!, + }), + framework: { + // fill missing configurations. + ...defaultConfigByFramework(framework.framework), + ...framework, + }, + asset_config: { asset_repository: MainImageRepository.instance }, + }); + + const src = postproc_src( + filesrc(code, framework.framework), + framework.language + ); + + return src; + } +} + +function filesrc( + code: Result, + framework: FrameworkConfig["framework"] +): string { + switch (framework) { + case "flutter": { + return code.code.raw; + } + default: + return code.scaffold.raw; + } +} + +function postproc_src(src: string, language: Language) { + if (language === Language.dart) { + const { code, error } = formatCode(src); + if (error) { + return src; + } + return code; + } + + return src; +} diff --git a/code/package.json b/code/package.json new file mode 100644 index 000000000..f49ba224b --- /dev/null +++ b/code/package.json @@ -0,0 +1,4 @@ +{ + "name": "@grida/code", + "version": "0.0.0" +} diff --git a/editor/next.config.js b/editor/next.config.js index 27c1d11c2..17e7557dc 100644 --- a/editor/next.config.js +++ b/editor/next.config.js @@ -20,6 +20,7 @@ const packages = [ // ----------------------------- // region @designto-code + "@grida/code", "@designto/debugger", "@grida/builder-config", "@grida/builder-config-preset", diff --git a/editor/pages/api/v1/code/index.ts b/editor/pages/api/v1/code/index.ts index f916f3c9f..ffb7f588f 100644 --- a/editor/pages/api/v1/code/index.ts +++ b/editor/pages/api/v1/code/index.ts @@ -1,17 +1,4 @@ -import { designToCode, Result } from "@designto/code"; -import { DesignInput } from "@grida/builder-config/input"; -import { parseFileAndNodeId } from "@design-sdk/figma-url"; -import { fetchTargetAsReflect } from "@design-sdk/figma-remote"; -import { - ImageRepository, - MainImageRepository, -} from "@design-sdk/asset-repository"; -import { RemoteImageRepositories } from "@design-sdk/figma-remote/asset-repository"; -import type { FrameworkConfig } from "@grida/builder-config"; -import { defaultConfigByFramework } from "@grida/builder-config-preset"; -import { Language } from "@grida/builder-platform-types"; -import { formatCode } from "dart-style"; - +import { code } from "@grida/code"; type FigmaAccessTokenType = "at" | "pat"; export default async function handler(req, res) { @@ -60,88 +47,3 @@ export default async function handler(req, res) { throw e; } } - -async function code({ - auth, - uri, - framework, -}: { - auth: - | { - personalAccessToken: string; - } - | { accessToken: string }; - uri: string; - framework: FrameworkConfig; -}) { - // - - const res = parseFileAndNodeId(uri as string); - if (res) { - const { file: filekey, node } = res; - - console.log(`filekey: ${filekey}`, `node: ${node}`); - - // - const target = await fetchTargetAsReflect({ - file: filekey, - node, - auth: auth, - }); - - MainImageRepository.instance = new RemoteImageRepositories(target.file, { - authentication: auth, - }); - MainImageRepository.instance.register( - new ImageRepository( - "fill-later-assets", - "grida://assets-reservation/images/" - ) - ); - - const code = await designToCode({ - input: DesignInput.fromApiResponse({ - raw: target.raw, - entry: target.reflect!, - }), - framework: { - // fill missing configurations. - ...defaultConfigByFramework(framework.framework), - ...framework, - }, - asset_config: { asset_repository: MainImageRepository.instance }, - }); - - const src = postproc_src( - filesrc(code, framework.framework), - framework.language - ); - - return src; - } -} - -function filesrc( - code: Result, - framework: FrameworkConfig["framework"] -): string { - switch (framework) { - case "flutter": { - return code.code.raw; - } - default: - return code.scaffold.raw; - } -} - -function postproc_src(src: string, language: Language) { - if (language === Language.dart) { - const { code, error } = formatCode(src); - if (error) { - return src; - } - return code; - } - - return src; -} diff --git a/package.json b/package.json index b1d22965a..d79361fc1 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,36 @@ { - "name": "designto.codes", - "homepage": "https://designto.codes/", - "repository": "https://github.com/gridaco/designto-code", - "license": "Apache 2.0", - "private": true, - "scripts": { - "checkout:all": "git submodule update --init --recursive", - "clean": "yarn lerna clean -y && yarn lerna bootstrap && yarn lerna exec 'rm -rf ./dist'", - "editor": "turbo run editor#dev", - "build": "turbo run build", - "lerna": "lerna" - }, - "workspaces": [ - "editor", - "editor-packages/*", - "packages/*", - "ui/editor-ui/packages/*" - ], - "resolutions": { - "@design-sdk/figma-node": "0.0.25", - "@reflect-ui/core": "0.0.9", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "@types/react": "18.0.24", - "@types/react-dom": "^18.0.8", - "csstype": "3.1.0" - }, - "devDependencies": { - "@types/node": "^18.6.3", - "lerna": "^4.0.0", - "turbo": "^1.4.2", - "typescript": "^4.7.4" - } -} \ No newline at end of file + "name": "designto.codes", + "homepage": "https://designto.codes/", + "repository": "https://github.com/gridaco/designto-code", + "license": "Apache 2.0", + "private": true, + "scripts": { + "checkout:all": "git submodule update --init --recursive", + "clean": "yarn lerna clean -y && yarn lerna bootstrap && yarn lerna exec 'rm -rf ./dist'", + "editor": "turbo run editor#dev", + "build": "turbo run build", + "lerna": "lerna" + }, + "workspaces": [ + "editor", + "code", + "editor-packages/*", + "packages/*", + "ui/editor-ui/packages/*" + ], + "resolutions": { + "@design-sdk/figma-node": "0.0.25", + "@reflect-ui/core": "0.0.9", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "@types/react": "18.0.24", + "@types/react-dom": "^18.0.8", + "csstype": "3.1.0" + }, + "devDependencies": { + "@types/node": "^18.6.3", + "lerna": "^4.0.0", + "turbo": "^1.4.2", + "typescript": "^4.7.4" + } +} From 5416b1e7346709e3d78fa5bcd32dc9e37c779d1f Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 11:26:31 +0900 Subject: [PATCH 02/11] add api/v1/embed endpoint for embedding use --- editor/pages/api/v1/embed/index.ts | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 editor/pages/api/v1/embed/index.ts diff --git a/editor/pages/api/v1/embed/index.ts b/editor/pages/api/v1/embed/index.ts new file mode 100644 index 000000000..e69077238 --- /dev/null +++ b/editor/pages/api/v1/embed/index.ts @@ -0,0 +1,38 @@ +import { Language } from "@grida/builder-platform-types"; +import { code } from "@grida/code"; +import { Request, Response } from "express"; + +export default async function handler(req: Request, res: Response) { + // get the access token from the query string + const { figma, fpat, fat } = req.query; + + if (!figma) { + res.status(400).send("

No figma file url is provided

"); + return; + } + + if (!fpat && !fat) { + res.status(400).send("

No figma access token is provided

"); + return; + } + + try { + const src = await code({ + uri: figma as string, + framework: { + framework: "preview", + imgage_alt: {}, + language: Language.html, + }, + auth: { + accessToken: fat as string, + personalAccessToken: fpat as string, + }, + }); + + res.status(200).send(src); + } catch (e) { + res.status(500).send(`

${e.message}

${e.stack}
`); + throw e; + } +} From 2ceca275ab196e7194b140f0731984b8551e2acb Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 13:55:32 +0900 Subject: [PATCH 03/11] init api pkg & types --- api/index.ts | 0 api/package.json | 4 +++ api/types.ts | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 api/index.ts create mode 100644 api/package.json create mode 100644 api/types.ts diff --git a/api/index.ts b/api/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/api/package.json b/api/package.json new file mode 100644 index 000000000..a63899008 --- /dev/null +++ b/api/package.json @@ -0,0 +1,4 @@ +{ + "name": "@grida/api", + "version": "0.0.0" +} diff --git a/api/types.ts b/api/types.ts new file mode 100644 index 000000000..8c593236b --- /dev/null +++ b/api/types.ts @@ -0,0 +1,65 @@ +import type { + FrameworkConfig, + VanillaFrameworkConfig, +} from "@grida/builder-config"; + +export type CodeResponse = FigmaToVanillaResponse; + +export const LICENSE_CE = { + "ce-use-of-generated-code": "Public Domain", + "ce-use-of-engine-source": "Apache-2.0", + "ce-use-for-commercial": "AGPL-3.0", +}; + +export type ApiEngineInfo = { + name: "code.grida.co/api/v1"; + version: "2023.1.1"; + license: "AGPL-3.0"; +}; + +export type D2CSourceMap = {}; + +export type FigmaInputPong = { + /** + * the id of entry node. usually it is the root node. + */ + entry: string; + /** + * the full node tree, including only essential information. like size, position, etc. + */ + node: object; +}; + +export interface BaseFigmaInputResponse { + figma: FigmaInputPong; +} + +export interface BaseResponse { + framework: FrameworkConfig; + engine: ApiEngineInfo; + version: 0; // for now, there is no versioning + license: typeof LICENSE_CE; +} + +export interface BaseWebFrameworkResponse extends BaseResponse { + src: string; + srcdoc: string; + srcmap: D2CSourceMap; + files: { + [key: string]: { + content: string; + }; + }; + + thumbnail: string; +} + +export interface VanillaCodeResponse extends BaseWebFrameworkResponse { + framework: VanillaFrameworkConfig; +} + +export interface FigmaToVanillaResponse + extends VanillaCodeResponse, + BaseFigmaInputResponse { + figma: FigmaInputPong; +} From 52b59188ef9608ae5bf0aadfda7ad69b87add771 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 13:56:24 +0900 Subject: [PATCH 04/11] init test dir --- api/__test__/.gitignore | 0 api/index.ts | 1 + 2 files changed, 1 insertion(+) create mode 100644 api/__test__/.gitignore diff --git a/api/__test__/.gitignore b/api/__test__/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/api/index.ts b/api/index.ts index e69de29bb..eea524d65 100644 --- a/api/index.ts +++ b/api/index.ts @@ -0,0 +1 @@ +export * from "./types"; From f8ff169a5a0d53d0e5ac5d38c11e41d18c910138 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 14:35:27 +0900 Subject: [PATCH 05/11] update docs --- api/README.md | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 api/README.md diff --git a/api/README.md b/api/README.md new file mode 100644 index 000000000..27ddc7c17 --- /dev/null +++ b/api/README.md @@ -0,0 +1,136 @@ +# Grida Code API (Private Beta) + +## Usage (REST) + +### `POST /api/v1/code` + +Example + +```ts +import Axios from "axios"; + +const client = Axios.create({ + baseURL: "https://code.grida.co/api/v1", + headers: { + "Content-Type": "application/json", + "X-Figma-Access-Token": "figd_xxxxxxxxxx", + }, +}); + +client.post("code", { + // target node url formatted as https://www.figma.com/file/{fileKey}?node-id={nodeId} + // at this moment, the nodeId must be formatted as 00:00, not 00-00 + // Note: if you copy & paste the link from the fihma editor, you will get in the format of 00-00 (we are updating this) + figma: "https://www.figma.com/file/MikewnarPfXXXXX/?node-id=0%3A1", + framework: { + framework: "vanilla", // react, flutter, ... + }, +}); +``` + +#### Request Body + +**`body.figma`** + +An URL indicating the target node in Figma design. + +target node url formatted as `https://www.figma.com/file/{fileKey}?node-id={nodeId}` +at this moment, the nodeId must be formatted as `00:00`, not `00-00` url encoded value like `0%3A1` is also acceptable. + +Note: if you copy & paste the link from the fihma editor, you will get in the format of 00-00 (we are updating this) + +**`body.framework`** + +A Framework configuration used for generating code. Learn more at [Framework Configuration](https://grida.co/docs/cli#2-framework-configuration) + +#### Response + +```ts +// example of response +{ + warnings: [], + src: '', // do not use this + srcdoc: '......', + srcmap: { + // the mapping between the generated code and the design + // node-id : xpath + '123:123': '//div[@data-figma-node-id="123:123"]]' + }, + files:{ + 'index.hml': '......' + } + framework:{ + // the framework config used for generating code + // ... + }, + // (if the input design source is figma, you will get the brief summary about the used design) + figma:{ + file:{ + // #region original data ------ + name: "Exmaples", + lastModified: "2023-03-28T17:51:08Z", + thumbnailUrl: "https://s3-alpha.figma.com/thumbnails/dc85b86a-2502-4baa-a776-ce0972131a80", + version: "3247308401", + // #endregion original data ------ + } + + filekey: 'MikewnarPfXXXXX', + entry: '0:1', + json: "{}", + node: { + ... + } + } + engine: { + // the info of the engine used for generating code + name: 'code.grida.co/api/v1', + // the engibe version + version: '2023.1.1' + }, + // the preview image of the rendered html + thumbnail: 'https://xxx.s3.amazonaws.com/.../xxxxxx.png', + license: { + // the license info of the generated code and the api + // ... + } +} +``` + +- `warnings` - An array of warnings. If there is no warning, it will be an empty array. (This is usually a warning caused by poor design, which can be ignored) +- `src` - The generated code as a uri, a publicly accessible html file endpoint. +- `srcdoc` - The generated code as a bundled and concatenated string, which can be used to embed the code directly into your website. +- + +### `GET /api/v1/embed` + +We also provide an experimental embed API for embedding the generated code directly into your website with an iframe. + +Example + +```html + +``` + +#### URL Parameters + +**`figma`** +Same as `/v1/code` API's `figma` parameter. + +**`fpat`** or **`fat`** + +- fpat: Figma Personal Access Token (starting with `figd_`) +- fat: Figma Access Token + +Warning: For security reasons, we highly recommend using the Figma Access Token (which expires), instead of the Figma Personal Access Token (which never expires unless revoked). The Figma Personal Access Token is only used for development purpose. + +We are planning on providing a more secure way to use embed with the `fpat` in the future. + +The framework configuration for embed is `vanilla-preview` by default. We are planning on providing a way to customize the framework configuration in the future. + +## API Clients (Under Development) + +At this moment there is no publicly available API wrappers. If you are looking for use on your local machine, you van use [Grida CLI](https://grida.co/cli) From 2abc901bdd16be9c56c43ecf9b1eb458607f2040 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 14:49:00 +0900 Subject: [PATCH 06/11] update docs with types --- api/README.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/api/README.md b/api/README.md index 27ddc7c17..03d9428ce 100644 --- a/api/README.md +++ b/api/README.md @@ -30,8 +30,24 @@ client.post("code", { #### Request Body +```ts +interface CodeRequets { + figma: FigmaNodeInput; + framework: Partial; +} +``` + **`body.figma`** +```ts +type FigmaNodeInput = + | string + | { url: string; version: string } + | { filekey: string; node: string; version: string }; +``` + +**Note** currently only `string` is supported. + An URL indicating the target node in Figma design. target node url formatted as `https://www.figma.com/file/{fileKey}?node-id={nodeId}` @@ -99,7 +115,6 @@ A Framework configuration used for generating code. Learn more at [Framework Con - `warnings` - An array of warnings. If there is no warning, it will be an empty array. (This is usually a warning caused by poor design, which can be ignored) - `src` - The generated code as a uri, a publicly accessible html file endpoint. - `srcdoc` - The generated code as a bundled and concatenated string, which can be used to embed the code directly into your website. -- ### `GET /api/v1/embed` @@ -131,6 +146,10 @@ We are planning on providing a more secure way to use embed with the `fpat` in t The framework configuration for embed is `vanilla-preview` by default. We are planning on providing a way to customize the framework configuration in the future. +## Request / Response Types Declarations + +See [./types.ts](./types.ts) + ## API Clients (Under Development) At this moment there is no publicly available API wrappers. If you are looking for use on your local machine, you van use [Grida CLI](https://grida.co/cli) From 92395a373ea932a31e56bb480740938f1f06b776 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 14:49:15 +0900 Subject: [PATCH 07/11] add request type decs --- api/types.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/api/types.ts b/api/types.ts index 8c593236b..2797e0ca5 100644 --- a/api/types.ts +++ b/api/types.ts @@ -3,6 +3,16 @@ import type { VanillaFrameworkConfig, } from "@grida/builder-config"; +export type FigmaNodeInput = + | string + | { url: string; version: string } + | { filekey: string; node: string; version: string }; + +export interface CodeRequets { + figma: FigmaNodeInput; + framework: Partial; +} + export type CodeResponse = FigmaToVanillaResponse; export const LICENSE_CE = { @@ -19,7 +29,16 @@ export type ApiEngineInfo = { export type D2CSourceMap = {}; +export type FigmaOriginalFileData = { + name: string; + lastModified: string; + thumbnailUrl: string; + version: string; +}; + export type FigmaInputPong = { + file?: FigmaOriginalFileData; + filekey: string; /** * the id of entry node. usually it is the root node. */ @@ -28,6 +47,7 @@ export type FigmaInputPong = { * the full node tree, including only essential information. like size, position, etc. */ node: object; + json: string; }; export interface BaseFigmaInputResponse { @@ -39,6 +59,7 @@ export interface BaseResponse { engine: ApiEngineInfo; version: 0; // for now, there is no versioning license: typeof LICENSE_CE; + warnings: string[]; } export interface BaseWebFrameworkResponse extends BaseResponse { From e26365b9503e3fdd5667838abbecaeb8d0f5eca1 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 14:58:23 +0900 Subject: [PATCH 08/11] mv license --- api/license.ts | 5 +++++ api/types.ts | 13 +++---------- 2 files changed, 8 insertions(+), 10 deletions(-) create mode 100644 api/license.ts diff --git a/api/license.ts b/api/license.ts new file mode 100644 index 000000000..cac15b1ec --- /dev/null +++ b/api/license.ts @@ -0,0 +1,5 @@ +export const LICENSE_CE = { + "ce-use-of-generated-code": "Public Domain", + "ce-use-of-engine-source": "Apache-2.0", + "ce-use-for-commercial": "AGPL-3.0", +}; diff --git a/api/types.ts b/api/types.ts index 2797e0ca5..72960c167 100644 --- a/api/types.ts +++ b/api/types.ts @@ -2,25 +2,20 @@ import type { FrameworkConfig, VanillaFrameworkConfig, } from "@grida/builder-config"; +import { LICENSE_CE } from "./license"; export type FigmaNodeInput = | string | { url: string; version: string } | { filekey: string; node: string; version: string }; -export interface CodeRequets { +export interface CodeRequest { figma: FigmaNodeInput; framework: Partial; } export type CodeResponse = FigmaToVanillaResponse; -export const LICENSE_CE = { - "ce-use-of-generated-code": "Public Domain", - "ce-use-of-engine-source": "Apache-2.0", - "ce-use-for-commercial": "AGPL-3.0", -}; - export type ApiEngineInfo = { name: "code.grida.co/api/v1"; version: "2023.1.1"; @@ -67,9 +62,7 @@ export interface BaseWebFrameworkResponse extends BaseResponse { srcdoc: string; srcmap: D2CSourceMap; files: { - [key: string]: { - content: string; - }; + [key: string]: string; }; thumbnail: string; From 2a9407560f804648706fd9184870b96e1b215f62 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 14:58:30 +0900 Subject: [PATCH 09/11] update docs --- api/README.md | 2 +- api/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/README.md b/api/README.md index 03d9428ce..aa2fe44c2 100644 --- a/api/README.md +++ b/api/README.md @@ -31,7 +31,7 @@ client.post("code", { #### Request Body ```ts -interface CodeRequets { +interface CodeRequest { figma: FigmaNodeInput; framework: Partial; } diff --git a/api/index.ts b/api/index.ts index eea524d65..d6fcf1a56 100644 --- a/api/index.ts +++ b/api/index.ts @@ -1 +1,2 @@ export * from "./types"; +export * from "./license"; From 162d51b152624ba15b390dd6222f87efb8394cf8 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 14:58:53 +0900 Subject: [PATCH 10/11] update api response format --- editor/next.config.js | 1 + editor/pages/api/v1/code/index.ts | 112 +++++++++++++++++++++--------- package.json | 1 + 3 files changed, 80 insertions(+), 34 deletions(-) diff --git a/editor/next.config.js b/editor/next.config.js index 17e7557dc..e18ca9de2 100644 --- a/editor/next.config.js +++ b/editor/next.config.js @@ -20,6 +20,7 @@ const packages = [ // ----------------------------- // region @designto-code + "@grida/api", "@grida/code", "@designto/debugger", "@grida/builder-config", diff --git a/editor/pages/api/v1/code/index.ts b/editor/pages/api/v1/code/index.ts index ffb7f588f..48d1a00f4 100644 --- a/editor/pages/api/v1/code/index.ts +++ b/editor/pages/api/v1/code/index.ts @@ -1,49 +1,93 @@ import { code } from "@grida/code"; -type FigmaAccessTokenType = "at" | "pat"; +import type { CodeRequest, FigmaToVanillaResponse } from "@grida/api/types"; +import { LICENSE_CE } from "@grida/api"; +import assert from "assert"; +import { FrameworkConfig, VanillaFrameworkConfig } from "@grida/builder-config"; + +type FigmaAccessTokenType = "fat" | "fpat"; export default async function handler(req, res) { - // accept only post request - if (req.method !== "POST") { - res.status(405).json({ message: "method not allowed" }); - return; - } + try { + // accept only post request + if (req.method !== "POST") { + res.status(405).json({ message: "method not allowed" }); + return; + } - const figma_access_token: string = req.headers["x-figma-token"]; + const figma_access_token: string = req.headers["x-figma-token"]; - if (!figma_access_token) { - res.status(401).json({ - message: "No figma access token provided.", - }); - return; - } + if (!figma_access_token) { + res.status(401).json({ + message: "No figma access token provided.", + }); + return; + } - const figma_access_token_type: FigmaAccessTokenType = - figma_access_token.startsWith("figd") ? "pat" : "at"; + const figma_access_token_type: FigmaAccessTokenType = + figma_access_token.startsWith("figd") ? "fpat" : "fat"; - const { uri, framework } = req.body; - try { - const src = await code({ - uri, - framework, - auth: - figma_access_token_type === "at" - ? { - accessToken: figma_access_token, - } - : { - personalAccessToken: figma_access_token, - }, - }); + const { figma, framework } = req.body as CodeRequest; - res.status(200).json({ - src, - }); + assert(typeof figma === "string", "`body.figma` must be a string"); + + try { + const src = await code({ + uri: figma, + framework: framework as FrameworkConfig, + auth: + figma_access_token_type === "fat" + ? { + accessToken: figma_access_token, + } + : { + personalAccessToken: figma_access_token, + }, + }); + + const response: FigmaToVanillaResponse = { + figma: { + file: { + name: "", + lastModified: "", + thumbnailUrl: "", + version: "", + }, + filekey: "", + entry: "", + node: undefined, + json: "", + }, + framework: framework as VanillaFrameworkConfig, + src: "", + srcdoc: "", + srcmap: null, // TODO: + files: { + "index.html": src, + }, + thumbnail: null, // TODO: + engine: { + name: "code.grida.co/api/v1", + version: "2023.1.1", + license: "AGPL-3.0", + }, + version: 0, + license: LICENSE_CE, + warnings: [], + }; + + res.status(200).json(response); + } catch (e) { + res.status(500).json({ + message: e.message, + stacktrace: e.stack, + }); + + throw e; + } } catch (e) { res.status(500).json({ message: e.message, stacktrace: e.stack, }); - - throw e; } } diff --git a/package.json b/package.json index d79361fc1..3f458a19f 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "workspaces": [ "editor", "code", + "api", "editor-packages/*", "packages/*", "ui/editor-ui/packages/*" From 8e07f97b2987c3e28d9886679ad5551733d3baf5 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 30 Mar 2023 15:22:10 +0900 Subject: [PATCH 11/11] update api response --- api/types.ts | 2 +- code/index.ts | 11 +++++--- editor/pages/api/v1/code/index.ts | 43 +++++++++++++++++++----------- editor/pages/api/v1/embed/index.ts | 2 +- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/api/types.ts b/api/types.ts index 72960c167..c60db8c6b 100644 --- a/api/types.ts +++ b/api/types.ts @@ -42,7 +42,7 @@ export type FigmaInputPong = { * the full node tree, including only essential information. like size, position, etc. */ node: object; - json: string; + json: object; }; export interface BaseFigmaInputResponse { diff --git a/code/index.ts b/code/index.ts index 91d17e06c..91fd77601 100644 --- a/code/index.ts +++ b/code/index.ts @@ -31,8 +31,6 @@ export async function code({ if (res) { const { file: filekey, node } = res; - console.log(`filekey: ${filekey}`, `node: ${node}`); - // const target = await fetchTargetAsReflect({ file: filekey, @@ -68,7 +66,14 @@ export async function code({ framework.language ); - return src; + return { + src, + figma: { + filekey, + node, + }, + target, + }; } } diff --git a/editor/pages/api/v1/code/index.ts b/editor/pages/api/v1/code/index.ts index 48d1a00f4..c4d260234 100644 --- a/editor/pages/api/v1/code/index.ts +++ b/editor/pages/api/v1/code/index.ts @@ -26,13 +26,13 @@ export default async function handler(req, res) { const figma_access_token_type: FigmaAccessTokenType = figma_access_token.startsWith("figd") ? "fpat" : "fat"; - const { figma, framework } = req.body as CodeRequest; + const { figma: figmaInput, framework } = req.body as CodeRequest; - assert(typeof figma === "string", "`body.figma` must be a string"); + assert(typeof figmaInput === "string", "`body.figma` must be a string"); try { - const src = await code({ - uri: figma, + const coderes = await code({ + uri: figmaInput, framework: framework as FrameworkConfig, auth: figma_access_token_type === "fat" @@ -44,22 +44,29 @@ export default async function handler(req, res) { }, }); + const { src: spchar_src, figma, target } = coderes; + + const src = replaceSpecialChars(spchar_src); + const response: FigmaToVanillaResponse = { figma: { - file: { - name: "", - lastModified: "", - thumbnailUrl: "", - version: "", - }, - filekey: "", - entry: "", - node: undefined, - json: "", + file: + // null, // TODO: + { + name: undefined, + lastModified: undefined, + thumbnailUrl: undefined, + version: undefined, + }, + + filekey: figma.filekey, + entry: figma.node, + node: target.figma, + json: target.remote, }, framework: framework as VanillaFrameworkConfig, - src: "", - srcdoc: "", + src: null, // TODO: + srcdoc: src, srcmap: null, // TODO: files: { "index.html": src, @@ -91,3 +98,7 @@ export default async function handler(req, res) { }); } } + +function replaceSpecialChars(input: string): string { + return input.replace(/\\t/g, "\t").replace(/\\n/g, "\n"); +} diff --git a/editor/pages/api/v1/embed/index.ts b/editor/pages/api/v1/embed/index.ts index e69077238..8cbfb324d 100644 --- a/editor/pages/api/v1/embed/index.ts +++ b/editor/pages/api/v1/embed/index.ts @@ -17,7 +17,7 @@ export default async function handler(req: Request, res: Response) { } try { - const src = await code({ + const { src } = await code({ uri: figma as string, framework: { framework: "preview",