Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add es Proxy for calling ExtendScript #83

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down
76 changes: 60 additions & 16 deletions src/js/lib/utils/bolt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ type ReturnType<F extends Function> = 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.
Expand Down Expand Up @@ -73,22 +82,17 @@ export const evalTS = <
functionName: Key,
...args: ArgTypes<Func>
): Promise<ReturnType<Func>> => {
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
Expand All @@ -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<Scripts[K]>
) => Promise<ReturnType<Scripts[K]>>;
};

export const evalFile = (file: string) => {
Expand Down
14 changes: 7 additions & 7 deletions src/js/main/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
evalFile,
openLinkInBrowser,
subscribeBackgroundColor,
evalTS,
es,
} from "../lib/utils/bolt";

import reactLogo from "../assets/react.svg";
Expand All @@ -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);
});
};
Expand Down
14 changes: 7 additions & 7 deletions src/js/template-react/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
evalFile,
openLinkInBrowser,
subscribeBackgroundColor,
evalTS,
es,
} from "../lib/utils/bolt";

import reactLogo from "../assets/react.svg";
Expand All @@ -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);
});
};
Expand Down
13 changes: 7 additions & 6 deletions src/js/template-svelte/main.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
openLinkInBrowser,
subscribeBackgroundColor,
evalTS,
es,
} from "../lib/utils/bolt";

import viteLogo from "../assets/vite.svg";
Expand All @@ -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);
});
};
Expand Down
13 changes: 7 additions & 6 deletions src/js/template-vue/main.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
openLinkInBrowser,
subscribeBackgroundColor,
evalTS,
es,
} from "../lib/utils/bolt";
import "../index.scss";

Expand All @@ -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);
});
};
Expand Down