├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── scripts
├── acrobat
│ ├── close_document.js
│ ├── new_document.js
│ ├── open_document.js
│ ├── save_and_close_document.js
│ ├── save_as_document.js
│ ├── save_document.js
│ └── select_document.js
├── after_effects
│ ├── close_document.js
│ ├── new_document.js
│ ├── open_document.js
│ ├── save_and_close_document.js
│ ├── save_as_document.js
│ └── save_document.js
├── animate
│ ├── close_document.js
│ ├── new_document.js
│ ├── open_document.js
│ ├── save_and_close_document.js
│ ├── save_as_document.js
│ ├── save_document.js
│ └── select_document.js
├── illustrator
│ ├── close_document.js
│ ├── new_document.js
│ ├── open_document.js
│ ├── save_and_close_document.js
│ ├── save_as_document.js
│ ├── save_document.js
│ └── select_document.js
├── indesign
│ ├── close_document.js
│ ├── new_document.js
│ ├── open_document.js
│ ├── save_and_close_document.js
│ ├── save_as_document.js
│ ├── save_document.js
│ └── select_document.js
└── photoshop
│ ├── close_document.js
│ ├── new_document.js
│ ├── open_document.js
│ ├── save_and_close_document.js
│ ├── save_as_document.js
│ ├── save_document.js
│ └── select_document.js
├── src
├── adobe-broadcast.ts
├── api.ts
├── app.ts
├── broadcast.ts
├── commands.ts
├── defaults.ts
├── index.ts
├── listener.ts
├── process.ts
├── script-builder.ts
└── script-file-creator.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 | .DS_Store
63 | dist/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | tsconfig.json
2 | node_modules/
3 | src/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 rkamysz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # adobe-node
2 | Control Adobe applications - such as Photoshop, Animate, Illustrator, InDesign - from node. Run JavaScript to remotely - from the command line - create, modify or export document content. This module can be used to automate the workflow by creating an action chain that can be executed without user intervention.
3 |
4 | More information about writing scripts for Adobe applications:
5 | - [Photoshop, Illustrator, InDesign etc. scripting center](https://www.adobe.com/devnet/scripting.html)
6 | - [Adobe Animate JavaScript API](https://help.adobe.com/archive/en_US/flash/cs5/flash_cs5_extending.pdf)
7 |
8 | ## Installation
9 |
10 | ```
11 | npm i adobe-node
12 | ```
13 |
14 | ## API
15 |
16 | ### Methods
17 | | Method | Arguments | Description |
18 | |:-------|:------------|:------------|
19 | | `init` | - | Initializes the `AdobeApp` instance/ starts Adobe Event Listener.
20 | | `on` | event: string,
callback: (message: BroadcastMessage) => void | Adds an event handler function to listen to an event so that when that event occurs, the callback will be triggered.
21 | | `runScript` | script: string,
options?: Options | Runs custom JavaScript
22 | | `selectDocument` | document: string | Brings selected document to the front of the screen.
23 | | `saveDocument` | ...documents: string[] | Saves changes.
24 | | `saveAsDocument` | document: string,
saveAs: string,
options: object | Saves the document in a specific format and location. Optionally you can specify the save options appropriate to the format.
_For more information on these options, see the script documentation for the selected Adobe product._
25 | | `openDocument` | ...documents: string[] | Opens documents.
26 | | `closeDocument` | ...documents: string[] | Closes documents.
27 | | `saveAndCloseDocument` | ...documents: string[] | Saves changes and closes the documents.
28 | | `newDocument` | options?: NewDocumentOptions | Creates document.
29 | | `open` | - | Opens the Adobe application.
30 | | `close` | - | Closes the Adobe application.
31 | | `dispose` | - | Closes Adobe Event Listener and rest of the `AdobeApp` components.
32 |
33 | ### Config
34 |
35 | #### Config
36 |
37 | | Method | Type | Description |
38 | |:-------|:------------|:------------|
39 | | `app` | AdobeAppConfig | Adobe application config
40 | | `host` | string | Domain name of the server.
41 | | `port` | number | Port number on which the server is listening.
42 | | `appTimeout` | number | Time after which the application process will close. Useful when the application freezes.
By default - `0` - this option is off.
43 | | `jsPath` | string | Location of the javaScript files.
Default path is `"./js"`
44 |
45 | #### AdobeAppConfig
46 |
47 | | Method | Type | Description |
48 | |:-------|:------------|:------------|
49 | | `name` | AdobeAppName | Adobe application name.
eg. `AdobeAppName.Photoshop`
50 | | `path` | string | Path to the Adobe's app executable file.
51 | | `adobeScriptsPath` | string | Location of the `Scripts` directory of the selected Adobe app.
52 |
53 | ### Others
54 |
55 | #### BroadcastMessage
56 |
57 | | Property | Type | Description |
58 | |:-------|:------------|:------------|
59 | | `command` | string | Command name
60 | | `stdout` | string | Standard output
61 | | `stderr` | string | Standard error
62 |
63 | #### NewDocumentOptions
64 |
65 | | Property | Type | Description |
66 | |:-------|:------------|:------------|
67 | | `title` | string | Name of the document
68 | | `width` | number | Document width
69 | | `height` | number | Document height
70 | | `...` | any | + other custom, optional properties
71 |
72 | ### Events
73 |
74 | | Event | Description |
75 | |:------|:------------|
76 | | JS script file name | the event for a specific function called by the `runScript()` method
77 | |`AdobeAppEvent.OpenApp` | -
78 | |`AdobeAppEvent.CloseApp` | -
79 | |`AdobeAppEvent.NewDocument` | -
80 | |`AdobeAppEvent.OpenDocument` | -
81 | |`AdobeAppEvent.CloseDocument` | -
82 | |`AdobeAppEvent.SelectDocument` | -
83 | |`AdobeAppEvent.SaveDocument` | -
84 | |`AdobeAppEvent.SaveAsDocument` | -
85 | |`AdobeAppEvent.SaveAndCloseDocument` | -
86 |
87 | ## Examples
88 |
89 | ### Basic example
90 |
91 | ```
92 | import { newAdobeApp, AdobeAppName, AdobeAppEvent, AdobeApp, BroadcastMessage } from "adobe-node";
93 |
94 | const sleep = (duration: number) => new Promise(resolve => { setTimeout(resolve, duration) });
95 |
96 | const main = async () => {
97 | const app: AdobeApp = newAdobeApp({
98 | app: {
99 | name: AdobeAppName.Photoshop,
100 | path: '/Applications/Adobe Photoshop CC 2019/Adobe Photoshop CC 2019.app/Contents/MacOS/Adobe Photoshop CC 2019',
101 | adobeScriptsPath: '/Applications/Adobe Photoshop CC 2019/Presets/Scripts'
102 | },
103 | host: 'localhost',
104 | port: 5000
105 | });
106 |
107 | app.on(AdobeAppEvent.OpenApp, () => {
108 | console.log(`The Adobe App is open`);
109 | })
110 | .on(AdobeAppEvent.NewDocument, () => {
111 | console.log(`The document has been created`);
112 | })
113 | .on(AdobeAppEvent.OpenDocument, (data: any) => {
114 | console.log(`The document has been opened`);
115 | })
116 | .on(AdobeAppEvent.CloseDocument, () => {
117 | console.log(`The document has been closed`);
118 | })
119 | .on(AdobeAppEvent.CloseApp, () => {
120 | console.log(`The Adobe App has been closed`);
121 | })
122 | .on("test_script", (message: BroadcastMessage) => {
123 | console.log(`Testing custom script - ${message}`);
124 | });
125 |
126 | app.init();
127 |
128 | await app.open();
129 | await app.openDocument('/test1.psd');
130 | await sleep(2000);
131 | await app.closeDocument('/test1.psd');
132 | await sleep(2000);
133 | await app.close();
134 | app.dispose();
135 | }
136 |
137 | main();
138 | ```
139 |
140 | ### Running custom scripts
141 | In Adobe applications you can run scripts in `JSFL` (Adobe Animate) and `JSX` (Photoshop, Illustrator etc.)
142 | One of the features of this module is the ability to run a custom scripts written in `javaScript`.
143 | There are a few things to explain, first of all this is the script template triggered in the selected Adobe application.
144 |
145 | #### Template of Adobe Script
146 |
147 | ```
148 | var ___{{__command__}} = (function() {
149 | var __stderr;
150 | var __stdout;
151 |
152 | try {
153 | {{__vars__}}
154 | __stdout = {{__fn__}}
155 | } catch (e) {
156 | __stderr = e;
157 | } finally {
158 | {{__broadcast__}}
159 | }
160 | })();
161 | ```
162 | Texts between `{{}}` are replaced with values prepared for a specific event/command. The javaScript code is pasted in the `{{__fn__}}` placeholder.
163 |
164 | As you can see `{{__fn__}}`/ JS code is assigned to the `__stdout` variable, this means that your javaScript code must be included in the `IIFE` function and it must also return a value - even if the logic doesn't require it - which will be passed in to the event.
165 |
166 | ```
167 | // IIFE example
168 | (function(){
169 | ... some magic
170 | return true; // whatever
171 | }());
172 | ```
173 |
174 | #### Running script without any arguments
175 |
176 | ```
177 | ...
178 | await app.runScript('/some_custom_script.js');
179 | ...
180 | ```
181 |
182 | #### Running script with arguments
183 |
184 | ```
185 | ...
186 | await app.runScript('/some_custom_script.js', {
187 | title: "New Document",
188 | width: 1024,
189 | height: 768
190 | });
191 | ...
192 | ```
193 |
194 | The arguments/ options used in the `runScript()` method are pasted in the `{{__vars__}}` placeholder.
195 | These vars are also available in the `IIFE`.
196 |
197 | #### Generated Adobe Script
198 |
199 | Here is an example of a generated script file that runs in Adobe app.
200 |
201 | ```
202 | var ___new_document = (function() {
203 | var __stderr;
204 | var __stdout;
205 |
206 | try {
207 | var title = "New Document";
208 | var width = 1024;
209 | var height = 768;
210 |
211 | __stdout = (function(){
212 | var doc = app.documents.add(width, height, 72, title, NewDocumentMode.RGB, DocumentFill.TRANSPARENT, 1);
213 | return true;
214 | }());
215 |
216 | } catch (e) {
217 | __stderr = e;
218 | } finally {
219 | app.system("adobe-broadcast --host='localhost' --port=5000 --msg='{\"command\":\"new_document\",\"stdout\":\"" + __stdout + "\", \"stderr\":\"" + __stderr + "\" }'");
220 | }
221 | })();
222 | ```
223 |
224 | The generated adobe scripts files - `jsx`/`jsfl` - are saved in the location specified in the `adobeScriptsPath` configuration.
225 |
226 | ## To Do
227 | - implement more and improve built-in scripts
228 |
229 | ## Changelog
230 |
231 | ## 2.0.0
232 | ### Added
233 | - __API__ `selectDocument()`
234 | - __API__ `saveAsDocument()`
235 | - __API__ `saveAndCloseDocument()`
236 | - Build-in scripts (currently only for Photoshop and Animate)
237 | - `adobe-node/scripts/photoshop|animate|illustrator|indesign|after_effects|acrobat/open_document.js`
238 | - `adobe-node/scripts/photoshop|animate|illustrator|indesign|after_effects|acrobat/new_document.js`
239 | - `adobe-node/scripts/photoshop|animate|illustrator|indesign|after_effects|acrobat/save_document.js`
240 | - `adobe-node/scripts/photoshop|animate|illustrator|indesign|after_effects|acrobat/save_as_document.js`
241 | - `adobe-node/scripts/photoshop|animate|illustrator|indesign|after_effects|acrobat/close_document.js`
242 | - `adobe-node/scripts/photoshop|animate|illustrator|indesign|after_effects|acrobat/save_and_close_document.js`
243 | - `adobe-node/scripts/photoshop|animate|illustrator|indesign|after_effects|acrobat/select_document.js`
244 |
245 | ### Changed
246 | - __API__ `saveDocument()` - removed optional argument `saveAs`, saving multiple documents in one call.
247 |
248 |
249 | ## Tips
250 |
251 | If the Adobe application does not start, try to:
252 |
253 | * Change the access/permissions settings to the directory set in the `adobeScriptsPath` param
254 | eg.
255 | ```
256 | sudo chown "/Applications/Adobe Photoshop CC 2019/Presets/Scripts/"
257 | ```
258 |
259 | * It is also possible that you will need to change user config to enable custom scripts.
260 | For example, for Photoshop, add `WarnRunningScripts 0` to the `PSUserConfig.txt` file in the Photoshop settings folder and restart Photoshop.
261 |
262 | If any of the built-in functions does not work as you expected, you can write these functions yourself and run them via the `runScript()` method.
263 |
264 |
265 |
266 | ## Contribute
267 | Feel free to contribute. If you have any ideas, requests just leave a message.
268 |
269 | ## Info
270 |
271 | I'm not using Adobe applications, so it may happen that one of the built-in scripts does not work properly or is not optimal. I wrote it based on available resources. If you find an error or you think the script should be written in a different way. Let me know or even better implement your solution and I will add it.
272 |
273 | ## License
274 |
275 | Copyright (c) 2019 Radoslaw Kamysz
276 |
277 | Licensed under the [MIT license](LICENSE).
278 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "adobe-node",
3 | "version": "1.1.2",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/minimist": {
8 | "version": "1.2.0",
9 | "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz",
10 | "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY="
11 | },
12 | "@types/node": {
13 | "version": "12.12.6",
14 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.6.tgz",
15 | "integrity": "sha512-FjsYUPzEJdGXjwKqSpE0/9QEh6kzhTAeObA54rn6j3rR4C/mzpI9L0KNfoeASSPMMdxIsoJuCLDWcM/rVjIsSA==",
16 | "dev": true
17 | },
18 | "minimist": {
19 | "version": "1.2.0",
20 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
21 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
22 | },
23 | "tslib": {
24 | "version": "1.10.0",
25 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
26 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "adobe-node",
3 | "version": "1.1.3",
4 | "description": "Control Adobe applications - such as Photoshop, Animate, Illustrator, InDesign - from node. Run JavaScript to remotely - from the command line - create, modify or export document content. This module can be used to automate the workflow by creating an action chain that can be executed without user intervention.",
5 | "main": "dist/index",
6 | "scripts": {
7 | "build": "tsc"
8 | },
9 | "bin": {
10 | "adobe-broadcast": "dist/adobe-broadcast.js"
11 | },
12 | "keywords": [
13 | "adobe",
14 | "photoshop",
15 | "animate",
16 | "illustrator",
17 | "indesign",
18 | "bridge",
19 | "pipeline",
20 | "jsx",
21 | "jsfl",
22 | "remote",
23 | "broadcast"
24 | ],
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/rkamysz/adobe-node.git"
28 | },
29 | "author": "Radoslaw Kamysz",
30 | "license": "MIT",
31 | "bugs": {
32 | "url": "https://github.com/rkamysz/adobe-node/issues"
33 | },
34 | "homepage": "https://github.com/rkamysz/adobe-node#readme",
35 | "devDependencies": {
36 | "@types/node": "^12.12.5"
37 | },
38 | "dependencies": {
39 | "@types/minimist": "^1.2.0",
40 | "minimist": "^1.2.0",
41 | "tslib": "^1.10.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/scripts/acrobat/close_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | var docs = app.activeDocs;
4 |
5 | for (var d in documents) {
6 | var path = documents[d];
7 | var name = path.replace(/^.*?([^\\\/]*)$/, '$1');
8 | for (var i=0; i < docs.length; i++) {
9 | var doc = docs[i];
10 | if (doc.info.Title == name) {
11 | doc.close(true);
12 | }
13 | }
14 | }
15 |
16 | return true;
17 |
18 | }(documents));
19 |
--------------------------------------------------------------------------------
/scripts/acrobat/new_document.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var _title = "new_document";
3 | var _width = 612;
4 | var _height = 792;
5 |
6 | try {
7 | _title = title;
8 | } catch (e) { }
9 | try {
10 | _width = width;
11 | } catch (e) { }
12 | try {
13 | _height = height;
14 | } catch (e) { }
15 |
16 |
17 | app.newDoc(_width, _height);
18 | return true;
19 | }());
20 |
--------------------------------------------------------------------------------
/scripts/acrobat/open_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | app.openDoc(documents[i]);
5 | }
6 |
7 | return true;
8 |
9 | }(documents));
--------------------------------------------------------------------------------
/scripts/acrobat/save_and_close_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | var docs = app.activeDocs;
4 |
5 | for (var d in documents) {
6 | var path = documents[d];
7 | var name = path.replace(/^.*?([^\\\/]*)$/, '$1');
8 | for (var i=0; i < docs.length; i++) {
9 | var doc = docs[i];
10 | if (doc.info.Title == name) {
11 | doc.saveAs(path);
12 | doc.close(true);
13 | }
14 | }
15 | }
16 |
17 | return true;
18 |
19 | }(documents));
20 |
--------------------------------------------------------------------------------
/scripts/acrobat/save_as_document.js:
--------------------------------------------------------------------------------
1 | (function (document, saveAs) {
2 | var name = document.replace(/^.*?([^\\\/]*)$/, '$1');
3 | var docs = app.activeDocs;
4 |
5 | for (var i = 0; i < docs.length; i++) {
6 | var doc = docs[i];
7 | if (doc.info.Title == name) {
8 | doc.saveAs(saveAs);
9 | }
10 | }
11 |
12 | return true;
13 |
14 | }(document, saveAs));
15 |
--------------------------------------------------------------------------------
/scripts/acrobat/save_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | var docs = app.activeDocs;
4 |
5 | for (var d in documents) {
6 | var path = documents[d];
7 | var name = path.replace(/^.*?([^\\\/]*)$/, '$1');
8 | for (var i=0; i < docs.length; i++) {
9 | var doc = docs[i];
10 | if (doc.info.Title == name) {
11 | doc.saveAs(path);
12 | }
13 | }
14 | }
15 |
16 | return true;
17 |
18 | }(documents));
--------------------------------------------------------------------------------
/scripts/acrobat/select_document.js:
--------------------------------------------------------------------------------
1 | (function (document) {
2 | var docs = app.activeDocs;
3 | var name = document.replace(/^.*?([^\\\/]*)$/, '$1');
4 |
5 | for (var i = 0; i < docs.length; i++) {
6 | var doc = docs[i];
7 | if (doc.info.Title == name) {
8 | doc.bringToFront();
9 | }
10 | }
11 |
12 | return true;
13 |
14 | }(document));
--------------------------------------------------------------------------------
/scripts/after_effects/close_document.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
3 | return true;
4 |
5 | }());
6 |
--------------------------------------------------------------------------------
/scripts/after_effects/new_document.js:
--------------------------------------------------------------------------------
1 | (function () {
2 |
3 | app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
4 | app.newProject();
5 |
6 | return true;
7 | }());
8 |
--------------------------------------------------------------------------------
/scripts/after_effects/open_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | app.open(new File(documents[0]));
4 |
5 | return true;
6 |
7 | }(documents));
8 |
--------------------------------------------------------------------------------
/scripts/after_effects/save_and_close_document.js:
--------------------------------------------------------------------------------
1 | (function () {
2 |
3 | app.project.close(CloseOptions.SAVE_CHANGES)
4 | return true;
5 |
6 | }());
7 |
--------------------------------------------------------------------------------
/scripts/after_effects/save_as_document.js:
--------------------------------------------------------------------------------
1 | (function (document, saveAs) {
2 |
3 | app.project.save(new File(saveAs));
4 |
5 | return true;
6 |
7 | }(document, saveAs));
8 |
--------------------------------------------------------------------------------
/scripts/after_effects/save_document.js:
--------------------------------------------------------------------------------
1 | (function () {
2 |
3 | app.project.save();
4 | return true;
5 |
6 | }());
--------------------------------------------------------------------------------
/scripts/animate/close_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | var name = documents[i].replace(/^.*?([^\\\/]*)$/, '$1');
5 | for(var i in fl.documents) {
6 | var doc = fl.documents[i];
7 | if(doc.name === name) {
8 | fl.closeDocument(doc, false);
9 | }
10 | }
11 | }
12 |
13 | return true;
14 |
15 | }(documents));
16 |
--------------------------------------------------------------------------------
/scripts/animate/new_document.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | fl.createDocument();
3 | return true;
4 | }());
5 |
--------------------------------------------------------------------------------
/scripts/animate/open_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | fl.openDocument(documents[i]);
5 | }
6 |
7 | return true;
8 |
9 | }(documents));
--------------------------------------------------------------------------------
/scripts/animate/save_and_close_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | var name = documents[i].replace(/^.*?([^\\\/]*)$/, '$1');
5 | for(var i in fl.documents) {
6 | var doc = fl.documents[i];
7 | if(doc.name === name) {
8 | doc.save();
9 | fl.closeDocument(doc, false);
10 | }
11 | }
12 | }
13 |
14 | return true;
15 |
16 | }(documents));
17 |
--------------------------------------------------------------------------------
/scripts/animate/save_as_document.js:
--------------------------------------------------------------------------------
1 | (function (document, saveAs) {
2 | var _selectionOnly = false;
3 |
4 | try {
5 | _selectionOnly = selectionOnly;
6 | } catch (e) { }
7 |
8 | var doc = fl.openDocument(document);
9 | doc.saveAs(saveAs, _selectionOnly);
10 |
11 | return true;
12 |
13 | }(document, saveAs));
14 |
--------------------------------------------------------------------------------
/scripts/animate/save_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 | for (var i in documents) {
3 | var doc = fl.openDocument(documents[i]);
4 | doc.save();
5 | }
6 | return true;
7 |
8 | }(documents));
--------------------------------------------------------------------------------
/scripts/animate/select_document.js:
--------------------------------------------------------------------------------
1 | (function(document){
2 |
3 | fl.openDocument(document);
4 |
5 | return true;
6 |
7 | }(document));
--------------------------------------------------------------------------------
/scripts/illustrator/close_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | var name = documents[i].replace(/^.*?([^\\\/]*)$/, '$1');
5 | app.documents[name].close(SaveOptions.DONOTSAVECHANGES);
6 | }
7 |
8 | return true;
9 |
10 | }(documents));
11 |
--------------------------------------------------------------------------------
/scripts/illustrator/new_document.js:
--------------------------------------------------------------------------------
1 | (function () {
2 |
3 | var _documentColorSpace = DocumentColorSpace.RGB;
4 | var _width = 500;
5 | var _height = 500;
6 | var _numArtBoards = 1;
7 | var _artboardLayout = 1;
8 | var _artboardSpacing = 1;
9 | var _artboardRowsOrCols = 1;
10 |
11 | try {
12 | _documentColorSpace = documentColorSpace;
13 | } catch (e) { }
14 | try {
15 | _width = width;
16 | } catch (e) { }
17 | try {
18 | _height = height;
19 | } catch (e) { }
20 | try {
21 | _numArtBoards = numArtBoards;
22 | } catch (e) { }
23 | try {
24 | _artboardLayout = artboardLayout;
25 | } catch (e) { }
26 | try {
27 | _artboardSpacing = artboardSpacing;
28 | } catch (e) { }
29 | try {
30 | _artboardRowsOrCols = artboardRowsOrCols;
31 | } catch (e) { }
32 |
33 | app.documents.add(_documentColorSpace, _width, _height, _numArtBoards, _artboardLayout, _artboardSpacing, _artboardRowsOrCols);
34 |
35 | return true;
36 | }());
37 |
--------------------------------------------------------------------------------
/scripts/illustrator/open_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | app.open(File(documents[i]));
5 | }
6 |
7 | return true;
8 |
9 | }(documents));
--------------------------------------------------------------------------------
/scripts/illustrator/save_and_close_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | var name = documents[i].replace(/^.*?([^\\\/]*)$/, '$1');
5 | app.documents[name].close(SaveOptions.SAVECHANGES);
6 | }
7 |
8 | return true;
9 |
10 | }(documents));
11 |
--------------------------------------------------------------------------------
/scripts/illustrator/save_as_document.js:
--------------------------------------------------------------------------------
1 | (function (document, saveAs) {
2 | var name = document.replace(/^.*?([^\\\/]*)$/, '$1');
3 |
4 | app.documents[name].saveAs(new File(saveAs));
5 |
6 | return true;
7 |
8 | }(document, saveAs));
9 |
--------------------------------------------------------------------------------
/scripts/illustrator/save_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | var rootDoc = app.activeDocument;
4 |
5 | for (var i in documents) {
6 | var name = documents[i].replace(/^.*?([^\\\/]*)$/, '$1');
7 | var doc = app.documents[name];
8 | app.activeDocument = doc;
9 | doc.save();
10 | }
11 | app.activeDocument = rootDoc;
12 | return true;
13 |
14 | }(documents));
--------------------------------------------------------------------------------
/scripts/illustrator/select_document.js:
--------------------------------------------------------------------------------
1 | (function(document){
2 |
3 | var name = document.replace(/^.*?([^\\\/]*)$/, '$1');
4 | var doc = app.documents[name];
5 |
6 | app.activeDocument = doc;
7 |
8 | return true;
9 |
10 | }(document));
--------------------------------------------------------------------------------
/scripts/indesign/close_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | var name = documents[i].replace(/^.*?([^\\\/]*)$/, '$1');
5 | app.documents.itemByName(name).close(SaveOptions.DONOTSAVECHANGES);
6 | }
7 |
8 | return true;
9 |
10 | }(documents));
11 |
--------------------------------------------------------------------------------
/scripts/indesign/new_document.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var _title = "new_document";
3 | var _width = 500;
4 | var _height = 500;
5 | var _pageOrientation = PageOrientation.portrait;
6 | var _pagesPerDocument = 16
7 |
8 | try {
9 | _title = title;
10 | } catch (e) { }
11 | try {
12 | _width = width;
13 | } catch (e) { }
14 | try {
15 | _height = height;
16 | } catch (e) { }
17 | try {
18 | _pageOrientation = pageOrientation;
19 | } catch (e) { }
20 | try {
21 | _pagesPerDocument = pagesPerDocument;
22 | } catch (e) { }
23 |
24 | var doc = app.documents.add();
25 |
26 | with(doc.documentPreferences){
27 | pageHeight = _height;
28 | pageWidth = _width;
29 | pageOrientation = _pageOrientation;
30 | pagesPerDocument = _pagesPerDocument;
31 | }
32 |
33 | return true;
34 | }());
35 |
--------------------------------------------------------------------------------
/scripts/indesign/open_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | app.open(File(documents[i]));
5 | }
6 |
7 | return true;
8 |
9 | }(documents));
--------------------------------------------------------------------------------
/scripts/indesign/save_and_close_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | var path = documents[i];
5 | var name = path.replace(/^.*?([^\\\/]*)$/, '$1');
6 | var doc = app.documents.itemByName(name);
7 | if(doc.saved) {
8 | doc.close(SaveOptions.yes);
9 | } else {
10 | doc.close(SaveOptions.yes, File(path));
11 | }
12 | }
13 |
14 | return true;
15 |
16 | }(documents));
17 |
--------------------------------------------------------------------------------
/scripts/indesign/save_as_document.js:
--------------------------------------------------------------------------------
1 | (function (document, saveAs) {
2 |
3 | var name = document.replace(/^.*?([^\\\/]*)$/, '$1');
4 |
5 | app.documents.itemByName(name).save(new File(saveAs));
6 |
7 | return true;
8 |
9 | }(document, saveAs));
10 |
--------------------------------------------------------------------------------
/scripts/indesign/save_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | var rootDoc = app.activeDocument;
4 |
5 | for (var i in documents) {
6 | var path = documents[i];
7 | var name = path.replace(/^.*?([^\\\/]*)$/, '$1');
8 | var doc = app.documents.itemByName(name);
9 | app.activeDocument = doc;
10 |
11 | if (doc.saved) {
12 | doc.save();
13 | } else {
14 | doc.save(new File(path));
15 | }
16 | }
17 | app.activeDocument = rootDoc;
18 | return true;
19 |
20 | }(documents));
--------------------------------------------------------------------------------
/scripts/indesign/select_document.js:
--------------------------------------------------------------------------------
1 | (function(document){
2 |
3 | var name = document.replace(/^.*?([^\\\/]*)$/, '$1');
4 | var doc = app.documents.itemByName(name);
5 |
6 | app.activeDocument = doc;
7 |
8 | return true;
9 |
10 | }(document));
--------------------------------------------------------------------------------
/scripts/photoshop/close_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | var name = documents[i].replace(/^.*?([^\\\/]*)$/, '$1');
5 | app.documents[name].close(SaveOptions.DONOTSAVECHANGES);
6 | }
7 |
8 | return true;
9 |
10 | }(documents));
11 |
--------------------------------------------------------------------------------
/scripts/photoshop/new_document.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var _title = "new_document";
3 | var _width = 500;
4 | var _height = 500;
5 | var _resolution = 72;
6 | var _newDocumentMode = NewDocumentMode.RGB;
7 | var _initialFill = DocumentFill.TRANSPARENT;
8 | var _pixelAspectRatio = 1;
9 | var _bitsPerChannel = BitsPerChannelType.EIGHT;
10 |
11 | try {
12 | _title = title;
13 | } catch (e) { }
14 | try {
15 | _width = width;
16 | } catch (e) { }
17 | try {
18 | _height = height;
19 | } catch (e) { }
20 | try {
21 | _resolution = resolution;
22 | } catch (e) { }
23 | try {
24 | _newDocumentMode = newDocumentMode;
25 | } catch (e) { }
26 | try {
27 | _initialFill = initialFill;
28 | } catch (e) { }
29 | try {
30 | _pixelAspectRatio = pixelAspectRatio;
31 | } catch (e) { }
32 | try {
33 | _bitsPerChannel = bitsPerChannel;
34 | } catch (e) { }
35 |
36 | app.documents.add(_width, _height, _resolution, _title, _newDocumentMode, _initialFill, _pixelAspectRatio, _bitsPerChannel);
37 | return true;
38 | }());
39 |
--------------------------------------------------------------------------------
/scripts/photoshop/open_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | app.open(File(documents[i]));
5 | }
6 |
7 | return true;
8 |
9 | }(documents));
--------------------------------------------------------------------------------
/scripts/photoshop/save_and_close_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | for (var i in documents) {
4 | var name = documents[i].replace(/^.*?([^\\\/]*)$/, '$1');
5 | app.documents[name].close(SaveOptions.SAVECHANGES);
6 | }
7 |
8 | return true;
9 |
10 | }(documents));
11 |
--------------------------------------------------------------------------------
/scripts/photoshop/save_as_document.js:
--------------------------------------------------------------------------------
1 | (function (document, saveAs) {
2 | var _options = { typename: PhotoshopSaveOptions };
3 | var _asCopy = false;
4 | var _extensionType = Extension.NONE;
5 | var name = document.replace(/^.*?([^\\\/]*)$/, '$1');
6 |
7 | try {
8 | _options = options;
9 | } catch (e) { }
10 | try {
11 | _asCopy = asCopy;
12 | } catch (e) { }
13 | try {
14 | _extensionType = extensionType;
15 | } catch (e) { }
16 |
17 | app.documents[name].saveAs(File(saveAs), _options, _asCopy, _extensionType);
18 |
19 | return true;
20 |
21 | }(document, saveAs));
22 |
--------------------------------------------------------------------------------
/scripts/photoshop/save_document.js:
--------------------------------------------------------------------------------
1 | (function (documents) {
2 |
3 | var rootDoc = app.activeDocument;
4 |
5 | for (var i in documents) {
6 | var name = documents[i].replace(/^.*?([^\\\/]*)$/, '$1');
7 | var doc = app.documents[name];
8 | app.activeDocument = doc;
9 | doc.save();
10 | }
11 | app.activeDocument = rootDoc;
12 | return true;
13 |
14 | }(documents));
--------------------------------------------------------------------------------
/scripts/photoshop/select_document.js:
--------------------------------------------------------------------------------
1 | (function(document){
2 |
3 | var name = document.replace(/^.*?([^\\\/]*)$/, '$1');
4 | var doc = app.documents[name];
5 |
6 | app.activeDocument = doc;
7 |
8 | return true;
9 |
10 | }(document));
--------------------------------------------------------------------------------
/src/adobe-broadcast.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import { createConnection, connect, Socket } from "net";
4 | import * as minimist from 'minimist';
5 | import defaults from './defaults';
6 |
7 | const args = minimist(process.argv.slice(2));
8 |
9 | const host:string = args.host || defaults.host;
10 | const port:number = Number(args.port) || defaults.port;
11 | const message:string = args.msg;
12 | const client: Socket = connect({ host, port }, () => {
13 | console.log('Adobe Broadcast connected');
14 | });
15 |
16 | client.setEncoding('utf8');
17 |
18 | client.on('error', (error: Error) => {
19 | console.error(`Adobe Broadcast error: ${JSON.stringify(error)}`);
20 | });
21 |
22 | client.write(message, () => {
23 | process.exit();
24 | });
--------------------------------------------------------------------------------
/src/api.ts:
--------------------------------------------------------------------------------
1 |
2 | export interface AdobeApp {
3 | init(): void;
4 | on(event: string, callback: Function): AdobeApp;
5 | runScript(command: string, options?: Options): Promise;
6 | selectDocument(document: string): Promise;
7 | saveDocument(...documents: string[]): Promise;
8 | saveAsDocument(document: string, saveAs: string, options?: object): Promise;
9 | openDocument(...documents: string[]): Promise;
10 | closeDocument(...documents: string[]): Promise;
11 | saveAndCloseDocument(...documents: string[]): Promise;
12 | newDocument(options?: NewDocumentOptions): Promise
13 | open(): Promise;
14 | close(): Promise;
15 | dispose(): void;
16 | }
17 |
18 | export enum AdobeAppName {
19 | Animate = 'animate',
20 | Photoshop = 'photoshop',
21 | Illustrator = 'illustrator',
22 | InDesign = 'indesign',
23 | Acrobat = 'acrobat',
24 | AfterEffects = 'after_effects'
25 | }
26 |
27 | export enum AdobeAppScriptFileType {
28 | Jsfl = 'jsfl',
29 | Jsx = 'jsx'
30 | }
31 |
32 | export type AdobeProcessOptions = {
33 | timeout?: number;
34 | timeoutCallback?: Function;
35 | }
36 |
37 | export type Options = {
38 | [prop:string]:any
39 | }
40 |
41 | export type NewDocumentOptions = Options & {
42 | title?: string;
43 | width?: number,
44 | height?: number
45 | }
46 |
47 | export type AdobeAppConfig = {
48 | name: AdobeAppName;
49 | path: string;
50 | adobeScriptsPath: string;
51 | }
52 |
53 | export type Config = {
54 | app:AdobeAppConfig;
55 | host: string;
56 | port: number;
57 | appTimeout?: number;
58 | jsPath?: string;
59 | }
60 |
61 | export interface AdobeEventListener {
62 | addEventListener(event: string, callback: Function): void;
63 | start(): void;
64 | close(): void;
65 | }
66 |
67 | export interface AdobeAppProcess {
68 | create(script: string): void;
69 | kill(): void;
70 | run(commandPath: string): void;
71 | }
72 |
73 | export interface CommandStack {
74 | push(data: AdobeScriptCommand): void;
75 | resolve(commandName: string): Promise;
76 | }
77 |
78 | export interface AdobeScriptBuilder {
79 | setName(value: string): AdobeScriptBuilder;
80 | setVariables(value: string): AdobeScriptBuilder;
81 | setBody(value: string): AdobeScriptBuilder;
82 | setBroadcast(value: string): AdobeScriptBuilder;
83 | build(): string;
84 | }
85 |
86 | export interface CommandFileCreator {
87 | create(command: string, useBuiltInScript?: boolean, options?: Options): Promise;
88 | }
89 |
90 | export interface BroadcastBuilder {
91 | build(command: string): string;
92 | }
93 |
94 | export interface BroadcastMessage {
95 | command: string;
96 | stdout?: string;
97 | stderr?: string;
98 | }
99 |
100 | export interface AdobeScriptCommand {
101 | command: string;
102 | resolve: Function;
103 | reject: Function;
104 | }
105 |
106 | export enum AdobeAppEvent {
107 | OpenApp = "open_app",
108 | CloseApp = "close_app",
109 | NewDocument = "new_document",
110 | OpenDocument = "open_document",
111 | CloseDocument = "close_document",
112 | SaveAndCloseDocument = "save_and_close_document",
113 | SaveDocument = "save_document",
114 | SelectDocument = "select_document",
115 | SaveAsDocument = "save_as_document",
116 | RunScript = "run_script"
117 | }
118 |
--------------------------------------------------------------------------------
/src/app.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 | import { AdobeAppEvent, AdobeAppProcess, CommandFileCreator as AdobeScriptCreator, CommandStack, Config, NewDocumentOptions, Options, AdobeApp, AdobeEventListener } from "./api";
4 | import newAdobeAppListener from './listener';
5 | import newAdobeAppProcess from './process';
6 | import newCommandStack from './commands';
7 | import newAdobeScriptCreator from './script-file-creator';
8 | import { broadcast } from './broadcast';
9 |
10 | export const newAdobeApp = (config: Config, timeoutCallback?: Function): AdobeApp => {
11 |
12 | const scriptCreator: AdobeScriptCreator = newAdobeScriptCreator(config);
13 | const commandStack: CommandStack = newCommandStack();
14 | const eventListener: AdobeEventListener = newAdobeAppListener(config.host, config.port, eventListenerCallback);
15 | const appProcess: AdobeAppProcess = newAdobeAppProcess(config.app.path, appCloseCallback, {
16 | timeout: config.appTimeout,
17 | timeoutCallback
18 | });
19 |
20 | function eventListenerCallback(commandName: string) {
21 | commandStack.resolve(commandName);
22 | }
23 |
24 | function appCloseCallback() {
25 | broadcast(config.host, config.port, { command: AdobeAppEvent.CloseApp });
26 | }
27 |
28 | function useBuiltInScript(command: string): boolean {
29 | return command === AdobeAppEvent.CloseDocument
30 | || command === AdobeAppEvent.SaveDocument
31 | || command === AdobeAppEvent.SaveAndCloseDocument
32 | || command === AdobeAppEvent.SaveAsDocument
33 | || command === AdobeAppEvent.OpenDocument
34 | || command === AdobeAppEvent.NewDocument
35 | || command === AdobeAppEvent.SelectDocument;
36 | }
37 |
38 | const app: AdobeApp = {
39 | init() {
40 | eventListener.start();
41 | },
42 | on(event: string, callback: Function): AdobeApp {
43 | eventListener.addEventListener(event, callback);
44 | return app;
45 | },
46 |
47 | runScript: (command: string, options?: Options): Promise =>
48 | new Promise(async (resolve, reject) => {
49 | const commandPath: string = await scriptCreator.create(command, useBuiltInScript(command), options);
50 | commandStack.push({ command, resolve, reject });
51 | appProcess.run(commandPath);
52 | }),
53 |
54 | saveDocument: (...documents: string[]): Promise =>
55 | app.runScript(AdobeAppEvent.SaveDocument, { documents }),
56 |
57 | selectDocument: (document: string): Promise =>
58 | app.runScript(AdobeAppEvent.SelectDocument, { document }),
59 |
60 | saveAsDocument: (document: string, saveAs: string, options?: object): Promise =>
61 | app.runScript(AdobeAppEvent.SaveAsDocument, { document, saveAs, options }),
62 |
63 | openDocument: (...documents: string[]): Promise =>
64 | app.runScript(AdobeAppEvent.OpenDocument, { documents }),
65 |
66 | closeDocument: (...documents: string[]): Promise =>
67 | app.runScript(AdobeAppEvent.CloseDocument, { documents }),
68 |
69 | saveAndCloseDocument: (...documents: string[]): Promise =>
70 | app.runScript(AdobeAppEvent.SaveAndCloseDocument, { documents }),
71 |
72 | newDocument: (options?: NewDocumentOptions): Promise =>
73 | app.runScript(AdobeAppEvent.NewDocument, options),
74 |
75 | open: (): Promise => new Promise(async (resolve, reject) => {
76 | const scriptPath = await scriptCreator.create(AdobeAppEvent.OpenApp)
77 | appProcess.create(scriptPath);
78 | commandStack.push({
79 | command: AdobeAppEvent.OpenApp, resolve, reject
80 | });
81 | }),
82 |
83 | close: (): Promise => new Promise(async (resolve, reject) => {
84 | appProcess.kill();
85 | commandStack.push({
86 | command: AdobeAppEvent.CloseApp, resolve, reject
87 | });
88 | }),
89 | dispose: (): void => {
90 | eventListener.close();
91 | }
92 | }
93 |
94 | return app;
95 | }
96 |
--------------------------------------------------------------------------------
/src/broadcast.ts:
--------------------------------------------------------------------------------
1 | import { AdobeAppName, BroadcastBuilder, BroadcastMessage, Config } from "./api";
2 | import { exec } from 'child_process';
3 |
4 | const broadcastMethods: Map string> = new Map string>([
5 | [AdobeAppName.Animate, (cmd: string) => `FLfile.runCommandLine("${cmd}");`],
6 | [AdobeAppName.Photoshop, (cmd: string) => `app.system("${cmd}");`],
7 | [AdobeAppName.Illustrator, (cmd: string) => `app.system("${cmd}");`],
8 | ]);
9 |
10 | const buildBroadcastCommand = (host: string, port: number, message: string) =>
11 | `adobe-broadcast --host='${host}' --port=${port} --msg='${message}'`;
12 |
13 | export const newBroadcastBuilder = (config: Config): BroadcastBuilder => {
14 |
15 | return {
16 | build(command: string) {
17 | const payload: string = `{\\"command\\":\\"${command}\\",\\"stdout\\":\\"" + __stdout + "\\", \\"stderr\\":\\"" + __stderr + "\\" }`;
18 | const broadcast: (msg: string) => string = broadcastMethods.get(config.app.name);
19 | const cmd: string = buildBroadcastCommand(config.host, config.port, payload);
20 | return broadcast(cmd);
21 | }
22 | }
23 | }
24 |
25 | export const broadcast = (host: string, port: number, message: BroadcastMessage) => {
26 | const cmd: string = buildBroadcastCommand(host, port, JSON.stringify(message));
27 | exec(cmd);
28 | }
29 |
--------------------------------------------------------------------------------
/src/commands.ts:
--------------------------------------------------------------------------------
1 | import { AdobeScriptCommand, CommandStack } from "./api";
2 |
3 | const newCommandStack = (): CommandStack => {
4 |
5 | const stack: Map = new Map();
6 |
7 | return {
8 | push: (data: AdobeScriptCommand): Promise => new Promise(resolve => {
9 | if (!stack.has(data.command)) stack.set(data.command, []);
10 | stack.get(data.command).push(data);
11 | return resolve();
12 | }),
13 | resolve: (commandName: string): Promise => new Promise(resolve => {
14 | if (stack.has(commandName)) {
15 | const command = stack.get(commandName).shift();
16 | if(command) {
17 | command.resolve();
18 | }
19 | }
20 | return resolve();
21 | })
22 | }
23 | };
24 |
25 | export default newCommandStack;
--------------------------------------------------------------------------------
/src/defaults.ts:
--------------------------------------------------------------------------------
1 | const adobeScriptsPath: string = 'adobe-scripts';
2 | const scriptsPath: string = 'js';
3 | const host: string = 'localhost';
4 | const port: number = 5000;
5 |
6 | export default {
7 | adobeScriptsPath,
8 | scriptsPath,
9 | host,
10 | port
11 | }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app';
2 | export * from './api';
--------------------------------------------------------------------------------
/src/listener.ts:
--------------------------------------------------------------------------------
1 | import { AdobeEventListener, BroadcastMessage } from './api';
2 | import { Socket, Server, createServer } from 'net';
3 |
4 | const newAdobeAppListener = (host: string, port: number, callback: (commandName: string) => void): AdobeEventListener => {
5 |
6 | const callbacks: Map = new Map();
7 | let server: Server;
8 | let client:Socket;
9 |
10 | function connectionListener(socket: Socket) {
11 | client = socket;
12 | socket.on('data', (buffer: Buffer) => {
13 | const data: BroadcastMessage = JSON.parse(buffer.toString());
14 |
15 | if (callbacks.has(data.command)) {
16 | callbacks.get(data.command)(data.stdout, data.stderr);
17 | }
18 |
19 | callback(data.command);
20 | });
21 | }
22 |
23 | function disposeServer() {
24 | if(client) {
25 | client.end();
26 | }
27 | server = null;
28 | console.log(`Adobe Event Listener has been stopped at port ${port}`);
29 | }
30 |
31 | return {
32 | addEventListener: (event: string, callback: Function): void => {
33 | if (callbacks.has(event)) {
34 | console.warn(`${event} listener will be overwritten`);
35 | }
36 | callbacks.set(event, callback);
37 | },
38 | start: (): void => {
39 | if (server) return;
40 | server = createServer(connectionListener);
41 | server.listen(port, host, () => {
42 | console.log(`Adobe Event Listener running at ${host}:${port}`);
43 | });
44 | },
45 | close: (): void => {
46 | server.close(disposeServer);
47 | }
48 | }
49 | }
50 |
51 | export default newAdobeAppListener;
52 |
--------------------------------------------------------------------------------
/src/process.ts:
--------------------------------------------------------------------------------
1 | import * as os from "os";
2 | import { exec, ChildProcess, execFile, ExecException } from "child_process";
3 | import { AdobeProcessOptions, AdobeAppProcess } from "./api";
4 | import { existsSync } from 'fs';
5 |
6 | const newAdobeAppProcess = (appPath: string, closeCallback: Function, options?: AdobeProcessOptions): AdobeAppProcess => {
7 | let process: ChildProcess;
8 | const timeoutCallback: Function = options.timeoutCallback;
9 | const processTimeout: number = options.timeout || 0;
10 | const openCmd: string = os.platform() === "win32" ? "start" : "open -a";
11 |
12 | const createCallback = (execTime: number) => (error: ExecException, stdout: string, stderr: string) => {
13 | let becauseOfTimeout: boolean = Date.now() - execTime >= processTimeout && processTimeout > 0;
14 | if (becauseOfTimeout && timeoutCallback) {
15 | timeoutCallback(error);
16 | } else {
17 | closeCallback(stdout);
18 | }
19 | };
20 |
21 | return {
22 | create:(openAppScript: string): void => {
23 | const execFileCallback = createCallback(Date.now());
24 | if(!existsSync(appPath)) {
25 | throw new Error('Wrong app path');
26 | }
27 | process = execFile(appPath, [openAppScript], { timeout: processTimeout }, execFileCallback);
28 | },
29 | kill:(): void => {
30 | process.kill();
31 | },
32 | run:(commandPath: string): void => {
33 | exec(`${openCmd} ${appPath.replace(/ /g, "\\ ")} ${commandPath.replace(/ /g, "\\ ")}`);
34 | }
35 | }
36 | }
37 |
38 | export default newAdobeAppProcess;
--------------------------------------------------------------------------------
/src/script-builder.ts:
--------------------------------------------------------------------------------
1 | import { AdobeScriptBuilder } from './api';
2 |
3 | const commandTemplate: string = `
4 | var ___{{__command__}} = (function() {
5 | var __stderr;
6 | var __stdout;
7 |
8 | try {
9 | {{__vars__}}
10 | __stdout = {{__fn__}}
11 | } catch (e) {
12 | __stderr = e;
13 | } finally {
14 | {{__broadcast__}}
15 | }
16 | })();`;
17 |
18 | const newAdobeScriptBuilder = (): AdobeScriptBuilder => {
19 |
20 | let name: string;
21 | let vars: string;
22 | let body: string;
23 | let broadcast: string;
24 |
25 | const builder = {
26 | setName(value: string) {
27 | name = value;
28 | return builder;
29 | },
30 | setVariables(value: string) {
31 | vars = value;
32 | return builder;
33 | },
34 | setBody(value: string) {
35 | body = value;
36 | return builder;
37 | },
38 | setBroadcast(value: string) {
39 | broadcast = value;
40 | return builder;
41 | },
42 | build() {
43 | return commandTemplate
44 | .replace('{{__command__}}', name)
45 | .replace('{{__vars__}}', vars)
46 | .replace('{{__fn__}}', body)
47 | .replace('{{__broadcast__}}', broadcast);
48 | }
49 | }
50 |
51 | return builder;
52 | }
53 |
54 | export default newAdobeScriptBuilder;
--------------------------------------------------------------------------------
/src/script-file-creator.ts:
--------------------------------------------------------------------------------
1 | import * as path from "path";
2 | import * as fs from "fs";
3 | import { AdobeAppName, AdobeAppScriptFileType, CommandFileCreator, AdobeScriptBuilder, BroadcastBuilder, Config, Options } from "./api";
4 | import newAdobeScriptBuilder from './script-builder';
5 | import { newBroadcastBuilder } from './broadcast';
6 | import defaults from './defaults';
7 |
8 | const scriptingExtension: Map = new Map([
9 | [AdobeAppName.Animate, AdobeAppScriptFileType.Jsfl],
10 | [AdobeAppName.Photoshop, AdobeAppScriptFileType.Jsx],
11 | [AdobeAppName.Illustrator, AdobeAppScriptFileType.Jsx],
12 | [AdobeAppName.InDesign, AdobeAppScriptFileType.Jsx]
13 | ]);
14 |
15 | const newAdobeScriptFileCreator = (config: Config): CommandFileCreator => {
16 | const appName: AdobeAppName = config.app.name
17 | const jsPath: string = config.jsPath || defaults.scriptsPath;
18 | const adobeScriptsPath: string = config.app.adobeScriptsPath || defaults.adobeScriptsPath;
19 | const scriptBuilder: AdobeScriptBuilder = newAdobeScriptBuilder();
20 | const broadcastBuilder: BroadcastBuilder = newBroadcastBuilder(config);
21 |
22 | const buildVars = (args: any): string => {
23 | let list: string[] = [];
24 | let arg: any;
25 | for (let name in args) {
26 | arg = args[name];
27 |
28 | if (arg) {
29 | list.push(`var ${name}=${arg.constructor.name === "String"
30 | ? `"${arg}"`
31 | : arg.constructor.name === "Array" ? `${JSON.stringify(arg)}`
32 | : arg};`);
33 |
34 | } else {
35 | list.push(`var ${name};`);
36 | }
37 | }
38 |
39 | return `${list.length ? list.join('\n') : ''}`
40 | };
41 |
42 | const buildBody = (command: string, useBuiltInScript: boolean): string => {
43 | const scriptPath: string = path.join(jsPath, appName, `${command}.js`);
44 | const builtInScript: string = path.join(__dirname, '..', 'scripts', appName, `${command}.js`);
45 |
46 | if (useBuiltInScript && fs.existsSync(builtInScript)) {
47 | console.info(`Built-in Script file found: ${builtInScript}`);
48 | return fs.readFileSync(builtInScript).toString();
49 | }
50 |
51 | if (fs.existsSync(scriptPath)) {
52 | console.info(`Custom script file found: ${scriptPath}`);
53 | return fs.readFileSync(scriptPath).toString();
54 | }
55 |
56 | return `"";`;
57 | }
58 |
59 | const createFile = (command: string, content: string): Promise => new Promise((resolve, reject) => {
60 | const filePath: string = path.join(adobeScriptsPath, `${command}.${scriptingExtension.get(appName)}`);
61 | const fileDirname: string = path.dirname(filePath);
62 | if(fs.existsSync(fileDirname)) {
63 | fs.writeFile(filePath, content, "utf-8", (err) => {
64 | return err ? reject(err) : resolve(filePath)
65 | });
66 | } else {
67 | return reject(`The path (${fileDirname}) is not valid.`)
68 | }
69 | });
70 |
71 | return {
72 | create: (command: string, useBuiltInScript: boolean, args?: Options): Promise =>
73 | new Promise((resolve, reject) => {
74 | const variables: string = buildVars(args);
75 | const body: string = buildBody(command, useBuiltInScript);
76 | const broadcast: string = broadcastBuilder.build(command);
77 |
78 | let content: string = scriptBuilder
79 | .setName(command)
80 | .setVariables(variables)
81 | .setBody(body)
82 | .setBroadcast(broadcast)
83 | .build();
84 | return createFile(command, content).then(resolve).catch(reject);
85 | })
86 | }
87 | }
88 |
89 | export default newAdobeScriptFileCreator;
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "target": "es5",
5 | "lib": ["es7", "dom"],
6 | "module": "commonjs",
7 | "moduleResolution": "node",
8 | "declaration": true,
9 | "noImplicitAny": true,
10 | "sourceMap": true,
11 | "inlineSources": true,
12 | "experimentalDecorators": true,
13 | "emitDecoratorMetadata": true,
14 | "importHelpers": true,
15 | "noEmit" : false,
16 | "noEmitOnError": true
17 | },
18 | "include": [
19 | "./src/**/*.ts"
20 | ]
21 | }
--------------------------------------------------------------------------------