├── LICENSE ├── README.md ├── src ├── Compile.js ├── CompileUI.js └── lib │ ├── ArrayEx.js │ ├── Compile.estk.js │ ├── Compile.vscode.js │ ├── ESTKTemplate.js │ ├── FileEx.js │ ├── FolderEx.js │ ├── ObjectEx.js │ └── Os.js └── test ├── index.js └── test files ├── test 1.js └── test 2.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tomas Šinkūnas 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 | # Compile # 2 | 3 | A helper class for Adobe ExtendScript to compile *.js and *.jsx files into *.jsxbin files. 4 | 5 | ### Prerequisites ### 6 | 7 | The class requires either [ExtendScript Toolkit](https://www.adobe.com/products/extendscript-toolkit.htmlESTK) application (that is EOLed on Mac), or [ExtendScript Debugger](https://marketplace.visualstudio.com/items?itemName=Adobe.extendscript-debug) extension for VSCode. 8 | 9 | * `Compile.estk()` requirements: [ExtendScript Toolkit](https://www.adobe.com/products/extendscript-toolkit.htmlESTK) application. 10 | * `Compile.vscode()` requirements: 11 | * [VSCode](https://code.visualstudio.com/Download) IDE, 12 | * [ExtendScript Debugger](https://marketplace.visualstudio.com/items?itemName=Adobe.extendscript-debug) extension for VSCode, 13 | * [Node](https://nodejs.org/en/download/) 10.11.0 or newer. 14 | 15 | ## Usage ## 16 | 17 | ```javascript 18 | // Inlcude the class 19 | #include 'Compile.js' 20 | 21 | 22 | 23 | // Compile using ExtendScript Toolkit application: 24 | Compile.estk( 25 | 'test.js', // Array of File or Folder objects to be compiled. Required. 26 | 'test.jsxbin', // A File or Folder object to save compiled file into. Optional. 27 | 28 | // Optional object with user defined parameters: 29 | { 30 | extension: '.jsxbin', // A custom extension. Optional. If not defined, uses .jsxbin. 31 | removeSource: false, // An option to remove source files. Optional. 32 | 33 | // A path to ExtenScript Toolkit application. Optional. If not defined, uses: 34 | // Mac: /Applications/Adobe ExtendScript Toolkit CC/ExtendScript Toolkit.app/Contents/MacOS/ExtendScript Toolkit 35 | // Win: C:\\Program Files (x86)\\Adobe\\Adobe ExtendScript Toolkit CC\\ExtendScript Toolkit.exe 36 | estk: '/Applications/Adobe ExtendScript Toolkit CC/ExtendScript Toolkit.app/Contents/MacOS/ExtendScript Toolkit', 37 | } 38 | ); 39 | 40 | 41 | 42 | // Compile using ExtendScript Debugger extension for VSCode: 43 | Compile.vscode( 44 | 'test.js', // Array of File or Folder objects to be compiled. Required. 45 | 'test.jsxbin', // A File or Folder object to save compiled file into. Optional. 46 | 47 | // Optional object with user defined parameters: 48 | { 49 | extension: '.jsxbin', // A custom extension. Optional. If not defined, uses .jsxbin. 50 | removeSource: false, // An option to remove source files. Optional. 51 | 52 | // A path to 'exportToJSXBin.js' file, residing in ExtendScript Debugger extension. Optional. 53 | exportToJSXBin: '~/.vscode/extensions/adobe.extendscript-debug-2.0.3/public-scripts/exportToJSXBin.js', 54 | 55 | // A path to node. Optional, if not defined, uses '/usr/local/bin/node'. 56 | node: '/usr/local/bin/node', 57 | } 58 | ); 59 | ``` 60 | 61 | --- 62 | 63 | **NOTE** 64 | 65 | `Compile.estk()` cannot be executed from VSCode or any other IDE if VSCode application is running - ESTK will throw error ExtendScript Toolkit CC already running! 66 | 67 | --- 68 | 69 | The following examples use `Compile.vscode()` method, which is identical if using `Compile.estk()` method. 70 | 71 | ### Case 1 ### 72 | 73 | ```javascript 74 | // Compile 'test.js' file with default parameters. 75 | // 76 | // Returns array with one File object 'test.jsxbin'. 77 | 78 | Compile.vscode('test.js'); 79 | 80 | // is the same as 81 | Compile.vscode('test.js', 'test.jsxbin'); 82 | ``` 83 | 84 | ### Case 2 ### 85 | 86 | ```javascript 87 | // Compile the contents of Input folder into Output folder. 88 | // 89 | // NOTE: 90 | // - Input folder objects are processed recursivelly 91 | // - Output folder does not maintain folder hierarchy 92 | // 93 | // Returns array of compiled File objects 94 | 95 | Compile.vscode('inputFolder', 'outputFolder'); 96 | ``` 97 | 98 | ### Case 3 ### 99 | ```javascript 100 | // Compile contents of inputFolder with user parameters: 101 | // - Compile the contents on folder in place, 102 | // - Change extension to '*.jsx', 103 | // - Remove source file. 104 | // 105 | // Returns array of compiled File objects 106 | 107 | Compile.vscode( 108 | 'inputFolder', 109 | undefined, { 110 | extension: '.jsx', 111 | removeSource: true, 112 | } 113 | ); 114 | ``` 115 | 116 | ## Using the UI ## 117 | ```javascript 118 | // Inlcude both Compile and CompileUI classes 119 | #include 'Compile.js' 120 | #include 'CompileUI.js' 121 | 122 | // Open the UI 123 | CompileUI.show(); 124 | ``` 125 | -------------------------------------------------------------------------------- /src/Compile.js: -------------------------------------------------------------------------------- 1 | var Compile = (function() { 2 | // @include 'lib/ArrayEx.js' 3 | // @include 'lib/ESTKTemplate.js' 4 | // @include 'lib/FileEx.js' 5 | // @include 'lib/FolderEx.js' 6 | // @include 'lib/ObjectEx.js' 7 | // @include 'lib/Os.js' 8 | 9 | var module = {}; 10 | 11 | // @include 'lib/Compile.estk.js' 12 | // @include 'lib/Compile.vscode.js' 13 | 14 | return module; 15 | })(); -------------------------------------------------------------------------------- /src/CompileUI.js: -------------------------------------------------------------------------------- 1 | var CompileUI = (function(thisObj) { 2 | // @include 'lib/ArrayEx.js' 3 | // @include 'lib/FileEx.js' 4 | // @include 'lib/Os.js' 5 | 6 | var script = { 7 | name: 'Compile', 8 | developer: 'Tomas Šinkūnas', 9 | developerURL: 'www.rendertom.com', 10 | }; 11 | 12 | var win; 13 | var minSpace = 2; 14 | 15 | var module = {}; 16 | module.build = function() { 17 | win = (thisObj instanceof Panel) ? thisObj : new Window('palette', script.name, undefined, { 18 | resizeable: true 19 | }); 20 | win.alignChildren = ['fill', 'top']; 21 | win.spacing = minSpace; 22 | win.onResizing = win.onResize = function() { 23 | this.layout.resize(); 24 | }; 25 | 26 | win.onShow = function() { 27 | vscode.radio.onClick(); 28 | radioCompileFiles.value = true; 29 | }; 30 | 31 | 32 | 33 | var grpCompiler = addPanel(win, 'Compiler'); 34 | 35 | var estk = buildCompilerSelector(grpCompiler, 'ExtendScript Toolkit'); 36 | estk.radio.helpTip = 'Compile using ExtendScript Toolkit'; 37 | estk.etPath.helpTip = 'Path to ExtendScript Tool application'; 38 | estk.etPath.text = Compile.getESTKPath(); 39 | estk.btnSearch.onClick = function() { 40 | var extension, file; 41 | 42 | extension = Os.isMac() ? 'app' : 'exe'; 43 | file = FileEx.selectFiles(extension, false, 'Please select ESTK.' + extension); 44 | if (file) { 45 | estk.etPath.text = file.fsName; 46 | } 47 | }; 48 | estk.radio.onClick = function() { 49 | estk.setSelected(true); 50 | 51 | vscode.setSelected(false); 52 | grpNodePath.enabled = false; 53 | }; 54 | 55 | 56 | 57 | var vscode = buildCompilerSelector(grpCompiler, 'VSCode'); 58 | vscode.radio.helpTip = 'Compile using VSCode and extension "ExtendScript Debugger"'; 59 | vscode.etPath.helpTip = 'Path to "exportToJSXBin.js" file\rin VSCode\'s extension "ExtendScript Debugger"'; 60 | vscode.etPath.text = Compile.getExportToJSXBinPath(); 61 | vscode.btnSearch.onClick = function() { 62 | var file = FileEx.selectFiles('js', false, 'Please select exportToJSXBin.js file'); 63 | if (file) { 64 | vscode.etPath.text = file.fsName; 65 | } 66 | }; 67 | vscode.radio.onClick = function() { 68 | vscode.setSelected(true); 69 | grpNodePath.enabled = true; 70 | 71 | estk.setSelected(false); 72 | }; 73 | var grpNodePath = addGroup(vscode.grpContainer); 74 | vscode.cbNodePath = grpNodePath.add('checkbox', undefined, 'Custom Node path'); 75 | vscode.cbNodePath.alignment = ['left', 'center']; 76 | vscode.cbNodePath.onClick = function() { 77 | vscode.etNodePath.enabled = this.value; 78 | }; 79 | vscode.etNodePath = grpNodePath.add('edittext'); 80 | vscode.etNodePath.helpTip = 'Node path. If not defined, uses "/usr/local/bin/node"'; 81 | vscode.etNodePath.enabled = vscode.cbNodePath.value; 82 | 83 | 84 | 85 | var grpSelection = addPanel(win, 'Selection'); 86 | var radioCompileFiles = grpSelection.add('radiobutton', undefined, 'Compile individual files'); 87 | var radioCompileFolder = grpSelection.add('radiobutton', undefined, 'Compile folder with files'); 88 | 89 | 90 | 91 | var grpOptions = addPanel(win, 'Options'); 92 | var grpExtension = addGroup(grpOptions); 93 | var cbExtension = grpExtension.add('checkbox', undefined, 'Custom extension'); 94 | cbExtension.alignment = ['left', 'center']; 95 | cbExtension.onClick = function() { 96 | etExtension.enabled = this.value; 97 | }; 98 | 99 | var etExtension = grpExtension.add('edittext', undefined, '.jsxbin'); 100 | etExtension.enabled = cbExtension.value; 101 | etExtension.preferredSize.width = 100; 102 | 103 | 104 | 105 | var grpOutputFolder = addGroup(grpOptions); 106 | 107 | var cbOutputFolder = grpOutputFolder.add('checkbox', undefined, 'Output Folder'); 108 | cbOutputFolder.alignment = ['left', 'center']; 109 | cbOutputFolder.onClick = function() { 110 | etOutputFolder.enabled = this.value; 111 | btnOutputFolder.enabled = this.value; 112 | }; 113 | 114 | var etOutputFolder = grpOutputFolder.add('edittext'); 115 | etOutputFolder.enabled = cbOutputFolder.value; 116 | etOutputFolder.preferredSize.width = 100; 117 | 118 | var btnOutputFolder = grpOutputFolder.add('button', undefined, '...'); 119 | btnOutputFolder.alignment = ['right', 'center']; 120 | btnOutputFolder.enabled = cbOutputFolder.value; 121 | btnOutputFolder.preferredSize = [24, 24]; 122 | btnOutputFolder.onClick = function() { 123 | var outputFolder = Folder.selectDialog('Select output folder'); 124 | if (outputFolder) { 125 | etOutputFolder.text = outputFolder.fsName; 126 | } 127 | }; 128 | 129 | 130 | 131 | var checkRemoveSource = grpOptions.add('checkbox', undefined, 'Remove source files'); 132 | 133 | 134 | 135 | var grpFooter = win.add('group'); 136 | grpFooter.alignChildren = ['fill', 'top']; 137 | grpFooter.margins.top = 5; 138 | grpFooter.spacing = minSpace; 139 | 140 | var btnRequirements = grpFooter.add('button', undefined, '?'); 141 | btnRequirements.alignment = ['left', 'top']; 142 | btnRequirements.onClick = showRequirements; 143 | btnRequirements.preferredSize.width = 28; 144 | 145 | var btnClose = grpFooter.add('button', undefined, 'Close'); 146 | btnClose.alignment = ['right', 'top']; 147 | btnClose.onClick = function() { 148 | win.close(); 149 | }; 150 | 151 | var btnOK = grpFooter.add('button', undefined, 'OK'); 152 | btnOK.alignment = ['right', 'top']; 153 | btnOK.onClick = function() { 154 | var compiler, files, options, outputFolder, selection; 155 | 156 | options = buildOptions(); 157 | 158 | try { 159 | if (estk.isSelected()) { 160 | compiler = Compile.estk; 161 | options.estk = validateESTKPath(estk); 162 | } else { 163 | compiler = Compile.vscode; 164 | options.exportToJSXBin = validateVSCodePath(vscode); 165 | if (vscode.cbNodePath.value) { 166 | options.node = vscode.etNodePath.text; 167 | } 168 | } 169 | 170 | selection = getSelection(); 171 | if (selection) { 172 | if (cbOutputFolder.value) { 173 | outputFolder = etOutputFolder.text; 174 | } 175 | 176 | files = compiler(selection, outputFolder, options); 177 | files = ArrayEx.filter(files, function(file) { 178 | return FileEx.getFileObject(file).exists; 179 | }); 180 | 181 | alert('Compiled ' + files.length + ' files'); 182 | } 183 | 184 | } catch (error) { 185 | handleError(error); 186 | } 187 | 188 | function buildOptions() { 189 | var options = {}; 190 | 191 | switch (true) { 192 | case cbExtension.value: 193 | options.extension = etExtension.text; 194 | break; 195 | case checkRemoveSource.value: 196 | options.removeSource = checkRemoveSource.value; 197 | break; 198 | } 199 | 200 | return options; 201 | } 202 | 203 | function getSelection() { 204 | if (radioCompileFiles.value) { 205 | return FileEx.selectFiles(['js', 'jsx'], true, 'Please select "*.js" or "*.jsx" files'); 206 | } else { 207 | return Folder.selectDialog('Select folder containing files to compile'); 208 | } 209 | } 210 | }; 211 | }; 212 | 213 | module.show = function() { 214 | if (!win || !isValid(win)) { 215 | module.build(); 216 | } 217 | 218 | if (win instanceof Window) { 219 | win.center(); 220 | win.show(); 221 | win.size.width = 300; 222 | } else { 223 | win.layout.layout(true); 224 | win.layout.resize(); 225 | } 226 | }; 227 | 228 | return module; 229 | 230 | function addGroup(parentGroup) { 231 | var group = parentGroup.add('group'); 232 | group.alignChildren = ['fill', 'center']; 233 | group.spacing = minSpace; 234 | 235 | return group; 236 | } 237 | 238 | function addPanel(parentGroup, name) { 239 | var panel = win.add('panel', undefined, name); 240 | panel.alignChildren = ['fill', 'top']; 241 | panel.spacing = minSpace; 242 | 243 | return panel; 244 | } 245 | 246 | function buildCompilerSelector(parentGroup, name) { 247 | var grpContainer = parentGroup.add('group'); 248 | grpContainer.alignChildren = ['fill', 'top']; 249 | grpContainer.margins.bottom = 5; 250 | grpContainer.orientation = 'column'; 251 | grpContainer.spacing = 0; 252 | 253 | var radio = grpContainer.add('radiobutton', undefined, name); 254 | 255 | var grpSearch = addGroup(grpContainer); 256 | 257 | var etPath = grpSearch.add('edittext'); 258 | var btnSearch = grpSearch.add('button', undefined, '...'); 259 | btnSearch.alignment = ['right', 'center']; 260 | btnSearch.preferredSize = [24, 24]; 261 | 262 | return { 263 | btnSearch: btnSearch, 264 | etPath: etPath, 265 | grpContainer: grpContainer, 266 | radio: radio, 267 | isSelected: function() { 268 | return radio.value; 269 | }, 270 | setSelected: function(value) { 271 | grpSearch.enabled = value; 272 | radio.value = value; 273 | } 274 | }; 275 | } 276 | 277 | function handleError(error) { 278 | var message = error.toString(); 279 | if (error instanceof Error) { 280 | message += '\nScript File: ' + File(error.fileName).displayName + 281 | '\nFunction: ' + arguments.callee.name + 282 | '\nError on Line: ' + error.line.toString(); 283 | } 284 | alert(message); 285 | } 286 | 287 | function showRequirements() { 288 | var win = new Window('dialog', 'Requirements', undefined, { 289 | resizeable: true, 290 | }); 291 | win.alignChildren = ['fill', 'fill']; 292 | win.onResizing = win.onResize = function() { 293 | this.layout.resize(); 294 | }; 295 | 296 | var text = '' + 297 | 'ExtendScript Toolkit requirements:\n' + 298 | ' - ExtendScript Toolkit application\n' + 299 | '\n' + 300 | 'Once installed, select ExtendScript Toolkit app file in the UI:\n' + 301 | 'Mac: /Applications/Adobe ExtendScript Toolkit CC/ExtendScript Toolkit.app\n' + 302 | 'Win: C:\\Program Files (x86)\\Adobe\\Adobe ExtendScript Toolkit CC\\ExtendScript Toolkit.exe\n' + 303 | '\n' + 304 | '-------------------------\n' + 305 | '\n' + 306 | 'VScode requirements:\n' + 307 | ' 1. VSCode:\n' + 308 | ' https://code.visualstudio.com/Download\n\n' + 309 | ' 2. ExtendScript Debugger extension for VSCode:\n' + 310 | ' https://marketplace.visualstudio.com/items?itemName=Adobe.extendscript-debug\n\n' + 311 | ' 3. Node 10.11.0 or newer:\n' + 312 | ' https://nodejs.org/en/download/\n' + 313 | '\n' + 314 | 'Once all installed, select "exportToJSXBin.js" file in the UI for VSCode: ' + 315 | '~/.vscode/extensions/adobe.extendscript-debug-2.0.3/public-scripts/exportToJSXBin.js\n\n' + 316 | 'If Node is installed in custom path, then define it in "Custom Node path", otherwise it defaults to "/usr/local/bin/node"\n' + 317 | '\n' + 318 | '-------------------------\n' + 319 | 'Developed by ' + script.developer + ', ' + script.developerURL; 320 | 321 | var et = win.add('edittext', undefined, text, { 322 | multiline: true, 323 | editable: true, 324 | }); 325 | et.preferredSize = [500, 300]; 326 | 327 | win.show(); 328 | } 329 | 330 | function validateESTKPath(estk) { 331 | var file, macPathSuffix, path; 332 | 333 | path = estk.etPath.text; 334 | macPathSuffix = '/Contents/MacOS/ExtendScript Toolkit'; 335 | if (Os.isMac() && !path.match(macPathSuffix)) { 336 | path += macPathSuffix; 337 | } 338 | 339 | file = FileEx.getFileObject(path); 340 | 341 | if (!file.exists) { 342 | estk.etPath.active = true; 343 | throw 'Not a valid ESTK path'; 344 | } 345 | 346 | return path; 347 | } 348 | 349 | function validateVSCodePath(vscode) { 350 | var file, fileName, path; 351 | 352 | path = vscode.etPath.text; 353 | 354 | file = FileEx.getFileObject(path); 355 | if (!file.exists) { 356 | vscode.etPath.active = true; 357 | throw 'File does not exist at path ' + file.fsName; 358 | } 359 | 360 | fileName = FileEx.getBaseName(path); 361 | if (!fileName.match(/exportToJSXBin/i)) { 362 | vscode.etPath.active = true; 363 | throw 'Not a valid VSCode path. Please select "exportToJSXBin.js" file'; 364 | } 365 | 366 | return path; 367 | } 368 | })(this); -------------------------------------------------------------------------------- /src/lib/ArrayEx.js: -------------------------------------------------------------------------------- 1 | var ArrayEx = (function() { 2 | var module = {}; 3 | 4 | module.castArray = function(array) { 5 | if (!module.isArray(array)) { 6 | array = [array]; 7 | } 8 | 9 | return array; 10 | }; 11 | 12 | module.filter = function(array, callback) { 13 | var filteredArray, index; 14 | 15 | filteredArray = []; 16 | index = 0; 17 | 18 | module.forEach(array, function(element, index, array) { 19 | if (callback(element, index, array)) { 20 | filteredArray[index++] = element; 21 | } 22 | }); 23 | 24 | return filteredArray; 25 | }; 26 | 27 | module.flat = function(array) { 28 | return Array.prototype.concat.apply([], array); 29 | }; 30 | 31 | module.forEach = function(array, callback) { 32 | for (var i = 0, il = array.length; i < il; i++) { 33 | callback(array[i], i, array); 34 | } 35 | }; 36 | 37 | module.isArray = function(array) { 38 | return Object.prototype.toString.call(array) === '[object Array]'; 39 | }; 40 | 41 | module.map = function(array, callback) { 42 | var mappedArray = []; 43 | module.forEach(array, function(item, i, array) { 44 | mappedArray[i] = callback(item, i, array); 45 | }); 46 | 47 | return mappedArray; 48 | }; 49 | 50 | return module; 51 | })(); -------------------------------------------------------------------------------- /src/lib/Compile.estk.js: -------------------------------------------------------------------------------- 1 | (function(module) { 2 | var estkOptions, estkPathMac, estkPathWin, options; 3 | 4 | estkPathMac = '/Applications/Adobe ExtendScript Toolkit CC/ExtendScript Toolkit.app/Contents/MacOS/ExtendScript Toolkit'; 5 | estkPathWin = 'C:\\Program Files (x86)\\Adobe\\Adobe ExtendScript Toolkit CC\\ExtendScript Toolkit.exe'; 6 | 7 | estkOptions = { 8 | estk: Os.isWindows() ? estkPathWin : estkPathMac, 9 | extension: '.jsxbin', 10 | removeSource: false, 11 | }; 12 | 13 | /** 14 | * Compiles list of files|folders into JSXBIN using ExtensScript Toolkit application. 15 | * @param {String[]|File[]} scriptPaths - array of File or Folder objects to be compiled. Required. 16 | * @param {String} [outputPath = source.jsxbin] - a File or Folder object save compiled file to. Optional. 17 | * @param {Object} [userOptions] - object with user parameters. 18 | * @param {String} [userOptions.estk = estkPathMac/estkPathWin] - path to ExtenScript Toolkit application. 19 | * @param {String} [userOptions.extension = '.jsxbin'] - a custom extension. Optional. If not defined, uses ".jsxbin". 20 | * @param {Boolean} [userOptions.removeSource = false] - an option to remove source files. Optional. 21 | */ 22 | module.estk = function(scriptPaths, outputPath, userOptions) { 23 | var cmd, ESTKApp, ESTKCompilerFile, outputPaths; 24 | 25 | options = ObjectEx.assign({}, [estkOptions, userOptions]); 26 | options.outputPath = outputPath; 27 | 28 | ESTKApp = FileEx.getFileObject(options.estk); 29 | if (!ESTKApp.exists) { 30 | throw 'Could not get ExtendScript Toolkit\nApplication does not exist at path ' + ESTKApp.fsName; 31 | } 32 | 33 | scriptPaths = getScriptPathsFromFolders(scriptPaths); 34 | outputPaths = getOutputPaths(scriptPaths); 35 | ESTKCompilerFile = buildESTKCompilerFile(scriptPaths, outputPaths); 36 | 37 | killESTK(); 38 | 39 | cmd = getESTKCommand(options.estk, ESTKCompilerFile); 40 | system.callSystem(cmd); 41 | 42 | ESTKCompilerFile.remove(); 43 | 44 | return ArrayEx.map(outputPaths, function(outputPath) { 45 | return FileEx.getFileObject(outputPath); 46 | }); 47 | }; 48 | 49 | module.getESTKPath = function() { 50 | return estkOptions.estk; 51 | }; 52 | 53 | 54 | 55 | function buildESTKCompilerFile(scriptPaths, outputPaths) { 56 | var ESTKCompilerFile, template; 57 | 58 | template = ESTKTemplate.toString(); 59 | 60 | template = template.replace(/'{{SCRIPT_FILES}}'/, scriptPaths.toSource()); 61 | template = template.replace(/'{{OUTPUT_FILES}}'/, outputPaths.toSource()); 62 | template = template.replace(/'{{REMOVE_SOURCE}}'/, options.removeSource); 63 | template = '//@target estoolkit#dbg\n(' + template + ')();'; 64 | 65 | ESTKCompilerFile = FileEx.write( 66 | FileEx.getFileObject(scriptPaths[0]).parent.fsName + '/ESTK Compiler File.js', 67 | template 68 | ); 69 | 70 | return ESTKCompilerFile; 71 | } 72 | 73 | function getOutputPaths(scriptPaths) { 74 | scriptPaths = ArrayEx.castArray(scriptPaths); 75 | return ArrayEx.map(scriptPaths, function(scriptPath) { 76 | return getOutputPath(scriptPath); 77 | }); 78 | 79 | function getOutputPath(scriptPath) { 80 | var extension, customPath, outputFile; 81 | 82 | extension = options.extension || '.jsxbin'; 83 | outputFile = FileEx.changeExtension(scriptPath, extension); 84 | 85 | if (options.outputPath) { 86 | customPath = options.outputPath; 87 | if (isFolder(customPath)) { 88 | customPath = FolderEx.getFolderObject(customPath).fsName + '/' + FileEx.getBaseName(scriptPath) + extension; 89 | } 90 | 91 | outputFile = FileEx.getFileObject(customPath); 92 | } 93 | 94 | return outputFile.fsName; 95 | } 96 | } 97 | 98 | function getESTKCommand(ESTKApp, ESTKCompilerFile) { 99 | var changeDirectory, cmd; 100 | 101 | ESTKApp = FileEx.getFileObject(ESTKApp); 102 | ESTKCompilerFile = FileEx.getFileObject(ESTKCompilerFile); 103 | 104 | changeDirectory = 'cd "' + ESTKCompilerFile.parent.fsName + '"'; 105 | cmd = changeDirectory + '; "' + ESTKApp.fsName + '" -cmd "' + ESTKCompilerFile.name + '"'; 106 | 107 | if (Os.isWindows()) { 108 | cmd = 'cmd /c ' + changeDirectory + ' && "' + ESTKApp.fsName + '" -cmd "' + ESTKCompilerFile.displayName + '"'; 109 | } 110 | 111 | return cmd; 112 | } 113 | 114 | function getScriptPathsFromFolders(scriptPaths, array) { 115 | array = array || []; 116 | scriptPaths = ArrayEx.castArray(scriptPaths); 117 | 118 | ArrayEx.forEach(scriptPaths, function(scriptPath) { 119 | var files, scriptFile; 120 | 121 | scriptFile = File(scriptPath); 122 | if (scriptFile instanceof File) { 123 | if (/(js|jsx|jsinc)$/i.test(FileEx.getExtension(scriptFile))) { 124 | array.push(scriptFile.fsName); 125 | } 126 | } else { 127 | files = scriptFile.getFiles(); 128 | return getScriptPathsFromFolders(files, array); 129 | } 130 | }); 131 | 132 | return array; 133 | } 134 | 135 | function isFolder(file) { 136 | return FileEx.getFileObject(file).name.indexOf('.') === -1; 137 | } 138 | 139 | function killESTK() { 140 | var cmd = 'osascript -e \'quit app "ExtendScript Toolkit"\''; 141 | if (Os.isWindows()) { 142 | cmd = 'START /wait taskkill /f /im "ExtendScript Toolkit.exe"'; 143 | } 144 | 145 | system.callSystem(cmd); 146 | } 147 | })(module); -------------------------------------------------------------------------------- /src/lib/Compile.vscode.js: -------------------------------------------------------------------------------- 1 | (function(module) { 2 | var options, vscodeOptions; 3 | 4 | vscodeOptions = { 5 | exportToJSXBin: '~/.vscode/extensions/adobe.extendscript-debug-2.0.3/public-scripts/exportToJSXBin.js', 6 | extension: '.jsxbin', 7 | node: '/usr/local/bin/node', 8 | removeSource: false, 9 | }; 10 | 11 | /** 12 | * Compiles list of files|folders into JSXBIN using ExtendScript Debugger extension for VSCode. 13 | * @param {String[]|File[]} scriptPaths - array of File or Folder objects to be compiled. Required. 14 | * @param {String} [outputPath = source.jsxbin] - a File or Folder object save compiled file to. Optional. 15 | * @param {Object} [userOptions] - object with user parameters. 16 | * @param {String} [userOptions.exportToJSXBin = '~/.vscode/extensions/adobe.extendscript-debug-2.0.3/public-scripts/exportToJSXBin.js'] - a path to 'exportToJSXBin.js' file, residing in ExtendScript Debugger extension. Optional. 17 | * @param {String} [userOptions.extension = '.jsxbin'] - a custom extension. Optional. If not defined, uses ".jsxbin". 18 | * @param {String} [userOptions.node = '/usr/local/bin/node'] - a path to node. Optional, if not defined, uses '/usr/local/bin/node'. 19 | * @param {Boolean} [userOptions.removeSource = false] - an option to remove source files. Optional. 20 | */ 21 | module.vscode = function(scriptPaths, outputPath, userOptions) { 22 | options = ObjectEx.assign({}, [vscodeOptions, userOptions]); 23 | options.outputPath = outputPath; 24 | 25 | var exportToJSXBin = FileEx.getFileObject(options.exportToJSXBin); 26 | if (!exportToJSXBin.exists) { 27 | throw 'Could not find "exportToJSXBin.js"\nFile does not exist at path ' + exportToJSXBin.fsName; 28 | } 29 | 30 | var node = FileEx.getFileObject(options.node); 31 | if (!node.exists) { 32 | throw 'Could not find node\nFile does not exist at path' + node.fsName; 33 | } 34 | 35 | scriptPaths = ArrayEx.castArray(scriptPaths); 36 | var outputPaths = ArrayEx.map(scriptPaths, function(scriptPath) { 37 | var cmd, res; 38 | 39 | cmd = getVSCODECommand(options.exportToJSXBin, scriptPath); 40 | res = system.callSystem(cmd); 41 | 42 | if (/Error/i.test(res)) { 43 | throw res; 44 | } 45 | 46 | return postProcessFile(scriptPath); 47 | }); 48 | 49 | return ArrayEx.flat(outputPaths); 50 | }; 51 | 52 | module.getExportToJSXBinPath = function() { 53 | return vscodeOptions.exportToJSXBin; 54 | }; 55 | 56 | 57 | 58 | function changeFileAttributes(scriptPath) { 59 | var extension, compiledFile, customPath, outputFile; 60 | 61 | extension = options.extension || '.jsxbin'; 62 | compiledFile = FileEx.changeExtension(scriptPath, '.jsxbin'); 63 | outputFile = FileEx.changeExtension(scriptPath, extension); 64 | 65 | if (options.outputPath) { 66 | customPath = options.outputPath; 67 | if (isFolder(customPath)) { 68 | customPath = FolderEx.getFolderObject(customPath).fsName + '/' + FileEx.getBaseName(scriptPath) + extension; 69 | } 70 | 71 | outputFile = FileEx.getFileObject(customPath); 72 | } 73 | 74 | if (compiledFile.fsName !== outputFile.fsName) { 75 | FileEx.copy(compiledFile, outputFile); 76 | compiledFile.remove(); 77 | } 78 | 79 | return outputFile; 80 | } 81 | 82 | function getVSCODECommand(exportToJSXBin, scriptFile) { 83 | exportToJSXBin = FileEx.getFileObject(exportToJSXBin); 84 | scriptFile = FileEx.getFileObject(scriptFile); 85 | 86 | return options.node + ' "' + 87 | exportToJSXBin.fsName + '" -f -n "' + 88 | scriptFile.fsName + '"'; 89 | } 90 | 91 | function isFolder(file) { 92 | return FileEx.getFileObject(file).name.indexOf('.') === -1; 93 | } 94 | 95 | function postProcessFile(scriptPath) { 96 | var file, files; 97 | 98 | file = FileEx.getFileObject(scriptPath); 99 | files = [file]; 100 | 101 | if (isFolder(file)) { 102 | files = FolderEx.getFiles(file, true, ['js', 'jsx']); 103 | } 104 | 105 | return ArrayEx.map(files, function(file) { 106 | if (options.removeSource) { 107 | file.remove(); 108 | } 109 | 110 | file = changeFileAttributes(file); 111 | 112 | return file; 113 | }); 114 | } 115 | })(module); -------------------------------------------------------------------------------- /src/lib/ESTKTemplate.js: -------------------------------------------------------------------------------- 1 | var ESTKTemplate = function() { 2 | try { 3 | 4 | var scriptPaths = '{{SCRIPT_FILES}}'; 5 | var outputPaths = '{{OUTPUT_FILES}}'; 6 | var removeSource = '{{REMOVE_SOURCE}}'; 7 | 8 | for (var i = 0, il = scriptPaths.length; i < il; i++) { 9 | exportJSXBIN(scriptPaths[i], outputPaths[i]); 10 | if (removeSource) { 11 | File(scriptPaths[i]).remove(); 12 | } 13 | } 14 | 15 | } catch (error) { 16 | alert(error); 17 | } 18 | 19 | function castFileObject(file) { 20 | return (file instanceof File) ? file : new File(file); 21 | } 22 | 23 | function exportJSXBIN(file, outputFile) { 24 | var compiledString, content, includePath; 25 | 26 | file = castFileObject(file); 27 | content = readFile(file); 28 | includePath = file.parent ? file.parent.absoluteURI : '/'; 29 | 30 | try { 31 | compiledString = app.compile(content, file.absoluteURI, includePath); 32 | } catch (error) { 33 | throw 'Unable to compile file ' + File.decode(file.name) + '\nFile path: ' + file.fsName + '\n' + e.toString() + '\nLine: ' + e.line.toString(); 34 | } 35 | 36 | return writeFile(outputFile, compiledString); 37 | } 38 | 39 | function readFile(file) { 40 | file = castFileObject(file); 41 | 42 | file.encoding = 'UTF-8'; 43 | file.open('r'); 44 | var content = file.read(); 45 | file.close(); 46 | 47 | return content; 48 | } 49 | 50 | function writeFile(file, content) { 51 | file = castFileObject(file); 52 | 53 | var folder = file.parent; 54 | if (!folder.exists && !folder.create()) { 55 | throw new Error('Could not create folder ' + folder.fsName); 56 | } 57 | 58 | file.encoding = 'UTF-8'; 59 | file.open('w'); 60 | var success = file.write(content); 61 | file.close(); 62 | 63 | if (!success) { 64 | throw new Error('Unable to write file ' + file.fsName); 65 | } 66 | 67 | return file; 68 | } 69 | }; -------------------------------------------------------------------------------- /src/lib/FileEx.js: -------------------------------------------------------------------------------- 1 | var FileEx = (function() { 2 | var isWindows = $.os.indexOf('Windows') > -1; 3 | var module = {}; 4 | 5 | module.changeExtension = function(file, newExtension) { 6 | file = module.getFileObject(file); 7 | return module.getFileObject( 8 | file.fsName.substring( 9 | 0, file.fsName.lastIndexOf('.') 10 | ) + newExtension 11 | ); 12 | }; 13 | 14 | module.copy = function(source, destination) { 15 | source = FileEx.getFileObject(source); 16 | destination = FileEx.getFileObject(destination); 17 | 18 | FolderEx.ensureFolderExists(destination.parent); 19 | 20 | var cmd = 'cp -a "' + source.fsName + '" "' + destination.fsName + '"'; 21 | if (isWindows) { 22 | cmd = 'cmd.exe /c" copy "' + source.fsName + '" "' + destination.fsName + '" /Y"'; 23 | } 24 | 25 | system.callSystem(cmd); 26 | }; 27 | 28 | module.getBaseName = function(file) { 29 | var name, baseName; 30 | 31 | file = module.getFileObject(file); 32 | 33 | name = File.decode(file.name); 34 | baseName = name.split('.').slice(0, -1).join('.'); 35 | 36 | return baseName; 37 | }; 38 | 39 | module.getExtension = function(file) { 40 | file = module.getFileObject(file); 41 | return file.fsName.split('.').pop(); 42 | }; 43 | 44 | module.getFileObject = function(file) { 45 | return (file instanceof File) ? file : new File(file); 46 | }; 47 | 48 | module.selectFiles = function(extensions, multiSelect, message) { 49 | extensions = ArrayEx.castArray(extensions); 50 | multiSelect = multiSelect || false; 51 | 52 | if (typeof message === 'undefined') { 53 | message = multiSelect ? 'Please select multiple files' : 'Please select file'; 54 | } 55 | 56 | var files = File.openDialog(message, getFilterForFiles(extensions), multiSelect); 57 | 58 | return files; 59 | 60 | function getFilterForFiles(extensions) { 61 | extensions = ArrayEx.castArray(extensions); 62 | 63 | if (Os.isWindows()) { 64 | return '*.' + extensions.join(';*.'); 65 | } 66 | 67 | var extensionListRe = '.(' + extensions.join('|') + ')$'; 68 | var re = new RegExp(extensionListRe, 'i'); 69 | 70 | return function(file) { 71 | return file.name.match(re) || file instanceof Folder; 72 | }; 73 | } 74 | }; 75 | 76 | module.write = function(file, contents, encoding, openMode) { 77 | file = module.getFileObject(file); 78 | FolderEx.ensureFolderExists(file.parent); 79 | 80 | encoding = encoding || 'UTF-8'; 81 | openMode = openMode || 'w'; // 'a', 'e', 'r', 'w'; 82 | 83 | file.encoding = encoding; 84 | file.open(openMode); 85 | var success = file.write(contents); 86 | file.close(); 87 | 88 | if (!success) { 89 | throw new Error('Unable to write file ' + file.fsName); 90 | } 91 | 92 | return file; 93 | }; 94 | 95 | return module; 96 | })(); -------------------------------------------------------------------------------- /src/lib/FolderEx.js: -------------------------------------------------------------------------------- 1 | var FolderEx = (function() { 2 | var module = {}; 3 | 4 | module.ensureFolderExists = function(folder) { 5 | folder = module.getFolderObject(folder); 6 | 7 | if (!folder.exists) { 8 | if (!folder.create()) { 9 | throw new Error('Could not create folder ' + folder.fsName); 10 | } 11 | } 12 | 13 | return folder; 14 | }; 15 | 16 | module.getFiles = function(folder, recursion, extensions, files) { 17 | var exetensionRegex, folderItems; 18 | 19 | files = files || []; 20 | folder = module.getFolderObject(folder); 21 | 22 | extensions = extensions || []; 23 | extensions = ArrayEx.castArray(extensions); 24 | exetensionRegex = new RegExp('.(' + extensions.join('|') + ')$', 'i'); 25 | 26 | folderItems = folder.getFiles(); 27 | ArrayEx.forEach(folderItems, function(file) { 28 | if (recursion && file instanceof Folder) { 29 | module.getFiles(file, recursion, extensions, files); 30 | } else { 31 | if (file.name.match(exetensionRegex)) { 32 | files.push(file); 33 | } 34 | } 35 | }); 36 | 37 | return files; 38 | }; 39 | 40 | module.getFolderObject = function(folder) { 41 | return (folder instanceof Folder) ? folder : new Folder(folder); 42 | }; 43 | 44 | return module; 45 | })(); -------------------------------------------------------------------------------- /src/lib/ObjectEx.js: -------------------------------------------------------------------------------- 1 | var ObjectEx = (function() { 2 | var module = {}; 3 | 4 | module.assign = function(target, sources) { 5 | if (typeof sources === 'undefined') { 6 | return target; 7 | } 8 | 9 | sources = ArrayEx.castArray(sources); 10 | 11 | ArrayEx.forEach(sources, function(source) { 12 | if (!module.isObject(source)) return; 13 | 14 | module.forOwn(source, function(value, key, object) { 15 | target[key] = value; 16 | }); 17 | }); 18 | 19 | return target; 20 | }; 21 | 22 | module.forOwn = function(object, callback) { 23 | for (var key in object) { 24 | if (object.hasOwnProperty(key)) { 25 | callback(object[key], key, object); 26 | } 27 | } 28 | }; 29 | 30 | module.isObject = function(object) { 31 | return (typeof object === 'object') && object !== null; 32 | }; 33 | 34 | return module; 35 | })(); -------------------------------------------------------------------------------- /src/lib/Os.js: -------------------------------------------------------------------------------- 1 | var Os = (function() { 2 | var module = {}; 3 | 4 | module.isMac = function() { 5 | return !module.isWindows(); 6 | }; 7 | 8 | module.isWindows = function() { 9 | return $.os.indexOf('Windows') !== -1; 10 | }; 11 | 12 | return module; 13 | })(); -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | (function(thisObj) { 2 | try { 3 | 4 | // @include '../src/Compile.js' 5 | // @include '../src/CompileUI.js' 6 | 7 | 8 | 9 | // Case 1: 10 | // Compile 'test.js' file with default parameters. 11 | // 12 | // Returns array with one File object 'test.jsxbin'. 13 | 14 | Compile.vscode('test.js'); 15 | 16 | // is the same as 17 | Compile.vscode('test.js', 'test.jsxbin'); 18 | 19 | 20 | 21 | // Case 2: 22 | // Compile the contents of Input folder into Output folder. 23 | // NOTE: 24 | // - Input folder objects are processed recursivelly 25 | // - Output folder does not maintain folder hierarchy 26 | // 27 | // Returns array of compiled File objects 28 | 29 | Compile.vscode('inputFolder', 'outputFolder'); 30 | 31 | 32 | 33 | // Case 3: 34 | // - Compile the contents on Input folder in place, 35 | // - Change extension to '*.jsx', 36 | // - Remove source file. 37 | // 38 | // Returns array of compiled File objects 39 | 40 | Compile.vscode( 41 | 'inputFolder', 42 | undefined, { 43 | extension: '.jsx', 44 | removeSource: true, 45 | } 46 | ); 47 | 48 | 49 | 50 | // Using UI 51 | CompileUI.show(); 52 | 53 | 54 | 55 | } catch (error) { 56 | var message = error.toString(); 57 | if (error instanceof Error) { 58 | message += '\nScript File: ' + File(error.fileName).displayName + 59 | '\nFunction: ' + arguments.callee.name + 60 | '\nError on Line: ' + error.line.toString(); 61 | } 62 | alert(message); 63 | } 64 | })(this); -------------------------------------------------------------------------------- /test/test files/test 1.js: -------------------------------------------------------------------------------- 1 | alert('this is test 1'); -------------------------------------------------------------------------------- /test/test files/test 2.js: -------------------------------------------------------------------------------- 1 | alert('this is test 2'); --------------------------------------------------------------------------------