From 15a4b36a00b466331861dc08e2077fdb905d054d Mon Sep 17 00:00:00 2001 From: Tim Haywood <48076776+timhaywood@users.noreply.github.com> Date: Thu, 22 Jun 2023 17:05:38 +1000 Subject: [PATCH 1/2] add `es` Proxy for calling ExtendScript --- README.md | 13 ++--- src/js/lib/utils/bolt.ts | 76 +++++++++++++++++++++++------- src/js/main/main.tsx | 14 +++--- src/js/template-react/main.tsx | 14 +++--- src/js/template-svelte/main.svelte | 13 ++--- src/js/template-vue/main.vue | 13 ++--- 6 files changed, 95 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 682bbb2..e19de8c 100644 --- a/README.md +++ b/README.md @@ -157,21 +157,22 @@ To add support for additional host apps: ## Calling ExtendScript from JS -All ExtendScript function are appended to your panel's namespace in the background to avoid namespace clashes when using `evalTS()` and `evalES()`. +All ExtendScript function are appended to your panel's namespace in the background to avoid namespace clashes when using `es` and `evalES()`. -We have now introduced a new and improved end-to-end type-safe way to interact with ExtendScript from CEP using `evalTS()`. This function dynamically infers types from -ExtendScript functions and handles both stringifying and parsing of the results so your developer interaction can be as simple as possible. +`bolt-cep` maintains an end-to-end type-safe way to interact with ExtendScript from CEP via the `es` object. This dynamically infers types from ExtendScript functions and handles both stringifying and parsing of the results, so your developer interaction can be as simple as possible. -As demonstrated in `main.tsx`, your ExtendScript functions can be called with `evalTS()` by passing the name of the function, followed by the arguments. +As demonstrated in `main.tsx`, your ExtendScript functions can be called as methods on the `es` object. Note their returns values are wrapped in a promise. + +Using "Go to Definition" on the `es` method calls in your CEP code will bring you to their ExtendScript implementations. CEP ```js -evalTS("myFunc", "test").then((res) => { +es.myFunc("test").then((res) => { console.log(res); }); -evalTS("myFuncObj", { height: 90, width: 100 }).then((res) => { +es.myFuncObj({ height: 90, width: 100 }).then((res) => { console.log(res.x); console.log(res.y); }); diff --git a/src/js/lib/utils/bolt.ts b/src/js/lib/utils/bolt.ts index b877539..f05c03a 100644 --- a/src/js/lib/utils/bolt.ts +++ b/src/js/lib/utils/bolt.ts @@ -43,6 +43,15 @@ type ReturnType = F extends (...args: infer A) => infer B ? B : never; +function getFormattedArgs(args: any[]) { + return args + .map((arg) => { + console.log(JSON.stringify(arg)); + return `${JSON.stringify(arg)}`; + }) + .join(","); +} + /** * @description End-to-end type-safe ExtendScript evaluation with error handling * Call ExtendScript functions from CEP with type-safe parameters and return types. @@ -73,22 +82,17 @@ export const evalTS = < functionName: Key, ...args: ArgTypes ): Promise> => { - return new Promise(function (resolve, reject) { - const formattedArgs = args - .map((arg) => { - console.log(JSON.stringify(arg)); - return `${JSON.stringify(arg)}`; - }) - .join(","); + const formattedArgs = getFormattedArgs(args); + return new Promise((resolve, reject) => csi.evalScript( `try{ - var host = typeof $ !== 'undefined' ? $ : window; - var res = host["${ns}"].${functionName}(${formattedArgs}); - JSON.stringify(res); - }catch(e){ - e.fileName = new File(e.fileName).fsName; - JSON.stringify(e); - }`, + var host = typeof $ !== 'undefined' ? $ : window; + var res = host["${ns}"].${functionName}(${formattedArgs}); + JSON.stringify(res); + }catch(e){ + e.fileName = new File(e.fileName).fsName; + JSON.stringify(e); + }`, (res: string) => { try { //@ts-ignore @@ -104,8 +108,48 @@ export const evalTS = < reject(res); } } - ); - }); + ) + ); +}; + +/** + * @description End-to-end type-safe ExtendScript evaluation with error handling. + * Call ExtendScript functions from CEP with type-safe parameters and return types. + * Any ExtendScript errors are captured and logged to the CEP console for tracing + * + * `es` is an object with all of your exported ExtendScript functions as properties. + * + * @example + * // CEP + * const res = await es.myFunc(60, 'test'); + * console.log(res.word); + * + * // ExtendScript + * export const myFunc = (num: number, word: string) => { + * return { num, word }; + * } + * + */ +export const es = new Proxy( + {}, + { + get(_, prop) { + // Protect against calling with symbols + if (typeof prop !== "string") { + throw TypeError("ExtendScript function name must be a string."); + } else { + // Return the evalTS function for the given prop name + // e.g. es.helloObj(...args) -> evalTS("hello", ...args) + return (...args: any[]) => evalTS(prop as keyof Scripts, args); + } + }, + } +) as { + // Cast to an object with keys of the exported script names + [K in keyof Scripts]: ( + // Each script is a function that accepts the corresponding args + ...args: ArgTypes + ) => Promise>; }; export const evalFile = (file: string) => { diff --git a/src/js/main/main.tsx b/src/js/main/main.tsx index 5fe0d8a..9e2b4fc 100644 --- a/src/js/main/main.tsx +++ b/src/js/main/main.tsx @@ -6,7 +6,7 @@ import { evalFile, openLinkInBrowser, subscribeBackgroundColor, - evalTS, + es, } from "../lib/utils/bolt"; import reactLogo from "../assets/react.svg"; @@ -31,24 +31,24 @@ const Main = () => { //* Demonstration of End-to-End Type-safe ExtendScript Interaction const jsxTestTS = () => { - evalTS("helloStr", "test").then((res) => { + es.helloStr("test").then((res) => { console.log(res); }); - evalTS("helloNum", 1000).then((res) => { + es.helloNum(1000).then((res) => { console.log(typeof res, res); }); - evalTS("helloArrayStr", ["ddddd", "aaaaaa", "zzzzzzz"]).then((res) => { + es.helloArrayStr(["ddddd", "aaaaaa", "zzzzzzz"]).then((res) => { console.log(typeof res, res); }); - evalTS("helloObj", { height: 90, width: 100 }).then((res) => { + es.helloObj({ height: 90, width: 100 }).then((res) => { console.log(typeof res, res); console.log(res.x); console.log(res.y); }); - evalTS("helloVoid").then(() => { + es.helloVoid().then(() => { console.log("function returning void complete"); }); - evalTS("helloError", "test").catch((e) => { + es.helloError("test").catch((e) => { console.log("there was an error", e); }); }; diff --git a/src/js/template-react/main.tsx b/src/js/template-react/main.tsx index 5fe0d8a..9e2b4fc 100644 --- a/src/js/template-react/main.tsx +++ b/src/js/template-react/main.tsx @@ -6,7 +6,7 @@ import { evalFile, openLinkInBrowser, subscribeBackgroundColor, - evalTS, + es, } from "../lib/utils/bolt"; import reactLogo from "../assets/react.svg"; @@ -31,24 +31,24 @@ const Main = () => { //* Demonstration of End-to-End Type-safe ExtendScript Interaction const jsxTestTS = () => { - evalTS("helloStr", "test").then((res) => { + es.helloStr("test").then((res) => { console.log(res); }); - evalTS("helloNum", 1000).then((res) => { + es.helloNum(1000).then((res) => { console.log(typeof res, res); }); - evalTS("helloArrayStr", ["ddddd", "aaaaaa", "zzzzzzz"]).then((res) => { + es.helloArrayStr(["ddddd", "aaaaaa", "zzzzzzz"]).then((res) => { console.log(typeof res, res); }); - evalTS("helloObj", { height: 90, width: 100 }).then((res) => { + es.helloObj({ height: 90, width: 100 }).then((res) => { console.log(typeof res, res); console.log(res.x); console.log(res.y); }); - evalTS("helloVoid").then(() => { + es.helloVoid().then(() => { console.log("function returning void complete"); }); - evalTS("helloError", "test").catch((e) => { + es.helloError("test").catch((e) => { console.log("there was an error", e); }); }; diff --git a/src/js/template-svelte/main.svelte b/src/js/template-svelte/main.svelte index d687fda..e49d616 100644 --- a/src/js/template-svelte/main.svelte +++ b/src/js/template-svelte/main.svelte @@ -8,6 +8,7 @@ openLinkInBrowser, subscribeBackgroundColor, evalTS, + es, } from "../lib/utils/bolt"; import viteLogo from "../assets/vite.svg"; @@ -32,24 +33,24 @@ //* Demonstration of End-to-End Type-safe ExtendScript Interaction const jsxTestTS = () => { - evalTS("helloStr", "test").then((res) => { + es.helloStr("test").then((res) => { console.log(res); }); - evalTS("helloNum", 1000).then((res) => { + es.helloNum(1000).then((res) => { console.log(typeof res, res); }); - evalTS("helloArrayStr", ["ddddd", "aaaaaa", "zzzzzzz"]).then((res) => { + es.helloArrayStr(["ddddd", "aaaaaa", "zzzzzzz"]).then((res) => { console.log(typeof res, res); }); - evalTS("helloObj", { height: 90, width: 100 }).then((res) => { + es.helloObj({ height: 90, width: 100 }).then((res) => { console.log(typeof res, res); console.log(res.x); console.log(res.y); }); - evalTS("helloVoid").then(() => { + es.helloVoid().then(() => { console.log("function returning void complete"); }); - evalTS("helloError", "test").catch((e) => { + es.helloError("test").catch((e) => { console.log("there was an error", e); }); }; diff --git a/src/js/template-vue/main.vue b/src/js/template-vue/main.vue index 3a4b4fc..5c4233e 100644 --- a/src/js/template-vue/main.vue +++ b/src/js/template-vue/main.vue @@ -8,6 +8,7 @@ import { openLinkInBrowser, subscribeBackgroundColor, evalTS, + es, } from "../lib/utils/bolt"; import "../index.scss"; @@ -21,24 +22,24 @@ const jsxTest = () => { //* Demonstration of End-to-End Type-safe ExtendScript Interaction const jsxTestTS = () => { - evalTS("helloStr", "test").then((res) => { + es.helloStr("test").then((res) => { console.log(res); }); - evalTS("helloNum", 1000).then((res) => { + es.helloNum(1000).then((res) => { console.log(typeof res, res); }); - evalTS("helloArrayStr", ["ddddd", "aaaaaa", "zzzzzzz"]).then((res) => { + es.helloArrayStr(["ddddd", "aaaaaa", "zzzzzzz"]).then((res) => { console.log(typeof res, res); }); - evalTS("helloObj", { height: 90, width: 100 }).then((res) => { + es.helloObj({ height: 90, width: 100 }).then((res) => { console.log(typeof res, res); console.log(res.x); console.log(res.y); }); - evalTS("helloVoid").then(() => { + es.helloVoid().then(() => { console.log("function returning void complete"); }); - evalTS("helloError", "test").catch((e) => { + es.helloError("test").catch((e) => { console.log("there was an error", e); }); }; From 0060ba4ba7325b041b2ca28b9befc10bbf99fdda Mon Sep 17 00:00:00 2001 From: Tim Haywood <48076776+timhaywood@users.noreply.github.com> Date: Fri, 23 Jun 2023 14:36:16 +1000 Subject: [PATCH 2/2] fix: es not passing args to evalTS correctly --- src/js/lib/utils/bolt.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/lib/utils/bolt.ts b/src/js/lib/utils/bolt.ts index f05c03a..b6e001b 100644 --- a/src/js/lib/utils/bolt.ts +++ b/src/js/lib/utils/bolt.ts @@ -140,7 +140,7 @@ export const es = new Proxy( } else { // Return the evalTS function for the given prop name // e.g. es.helloObj(...args) -> evalTS("hello", ...args) - return (...args: any[]) => evalTS(prop as keyof Scripts, args); + return (...args: any[]) => evalTS(prop as keyof Scripts, ...args); } }, }