├── .babelrc ├── .eslintrc ├── .gitignore ├── custom.d.ts ├── index.html ├── package.json ├── postcss.config.js ├── readme.md ├── src ├── appUtil │ ├── checker.ts │ ├── defaultConfig.ts │ ├── groupSettings.ts │ ├── ignoreArr.ts │ ├── menuTemplate.ts │ ├── openFolder.ts │ ├── parser.ts │ ├── securitySettingsInfo.ts │ ├── traverse.ts │ ├── tsestraverse.ts │ ├── tsmorph.ts │ └── versionFinder.ts ├── icons │ ├── Clipboard01.jpg │ ├── Clipboard02.jpg │ ├── Clipboard03.jpg │ ├── Clipboard04.jpg │ ├── GitHub-Mark-64px.png │ ├── deleteIcon.svg │ ├── faraday-logo.png │ ├── google-docs.svg │ ├── icon.svg │ ├── iconTransparent.svg │ ├── openFolder.svg │ └── testing.svg ├── main.ts ├── preload.ts ├── renderer │ ├── App.tsx │ ├── Slices │ │ ├── ignoreItemsSlice.ts │ │ ├── loadingSlice.tsx │ │ └── testResultSlice.ts │ ├── components │ │ ├── IgnoreCards.tsx │ │ ├── Loader.tsx │ │ ├── NavBar.tsx │ │ └── ResultDisplay.tsx │ ├── index.tsx │ └── store.ts └── styles.css ├── tailwind.config.js ├── tsconfig.json ├── webpack.config.js ├── webpack.electron.js └── webpack.react.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/env", 5 | { 6 | "targets": { 7 | "node": "6" 8 | } 9 | } 10 | ], 11 | "@babel/react" 12 | ] 13 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/typescript-estree", 3 | "plugins": ["@typescript-eslint"], 4 | "rules": { 5 | "@typescript-eslint/rule-name": "error" 6 | } 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /custom.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg" { 2 | const content: any; 3 | export default content; 4 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | FaradayJS 10 | 11 | 12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FaradayJS", 3 | "version": "1.0.0", 4 | "description": "A tool for securing your Electron Application!", 5 | "main": "dist/main.js", 6 | "scripts": { 7 | "start": "npm run build && electron dist/main.js", 8 | "devstart": "npm run build && electron ./dist/main.js", 9 | "dev:react": "concurrently \"webpack serve --hot --open\" ", 10 | "build": "webpack", 11 | "tsc:build": "tsc", 12 | "test": "mocha", 13 | "compile": "webpack --mode production", 14 | "build-installer": "electron-builder" 15 | }, 16 | "build": { 17 | "files": [ 18 | "dist/**/*", 19 | "package.json" 20 | ], 21 | "extraMetadata": { 22 | "main": "dist/main.js" 23 | }, 24 | "appId": "FaradayJS", 25 | "win": { 26 | "target": ["nsis"] 27 | }, 28 | "mac":{ 29 | "target":["dmg"] 30 | }, 31 | "linux":{ 32 | "target":["deb"] 33 | }, 34 | "nsis": { 35 | "oneClick": false, 36 | "allowToChangeInstallationDirectory": true 37 | }, 38 | "dmg":{ 39 | "oneClick": false, 40 | "allowToChangeInstallationDirectory": true 41 | }, 42 | "deb":{ 43 | "oneClick": false, 44 | "allowToChangeInstallationDirectory": true 45 | } 46 | }, 47 | "keywords": [], 48 | "author": "", 49 | "license": "ISC", 50 | "devDependencies": { 51 | "@babel/core": "^7.15.0", 52 | "@babel/preset-env": "^7.15.0", 53 | "@babel/preset-react": "^7.14.5", 54 | "@reduxjs/toolkit": "^1.6.1", 55 | "@types/estraverse": "^5.1.1", 56 | "@types/react": "^17.0.24", 57 | "@types/react-dom": "^17.0.9", 58 | "@types/react-router-dom": "^5.1.8", 59 | "@typescript-eslint/eslint-plugin": "^4.29.2", 60 | "@typescript-eslint/parser": "^4.31.1", 61 | "@typescript-eslint/typescript-estree": "^4.29.2", 62 | "assert": "^2.0.0", 63 | "autoprefixer": "^9.8.6", 64 | "babel-loader": "^8.2.2", 65 | "chai": "^4.3.4", 66 | "chai-as-promised": "^7.1.1", 67 | "cross-env": "^7.0.3", 68 | "css-loader": "^6.2.0", 69 | "electron": "^13.1.9", 70 | "electron-builder": "^22.13.1", 71 | "electron-devtools-installer": "^3.2.0", 72 | "electron-is-dev": "^2.0.0", 73 | "electron-webpack": "^2.8.2", 74 | "estraverse": "^5.2.0", 75 | "estraverse-jsx": "^1.4.0", 76 | "html-webpack-plugin": "^5.3.2", 77 | "htmlparser2": "^7.0.0", 78 | "jscodeshift": "^0.13.0", 79 | "mini-css-extract-plugin": "^2.2.0", 80 | "mocha": "^9.1.2", 81 | "postcss": "^7.0.36", 82 | "postcss-loader": "^6.1.1", 83 | "react-ast": "^0.3.1", 84 | "react-redux": "^7.2.5", 85 | "redux": "^4.1.1", 86 | "redux-devtools-extension": "^2.13.9", 87 | "spectron": "^15.0.0", 88 | "style-loader": "^3.2.1", 89 | "svg-url-loader": "^7.1.1", 90 | "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.7", 91 | "ts-loader": "^9.2.5", 92 | "ts-mocha": "^8.0.0", 93 | "ts-morph": "^12.0.0", 94 | "ts-node": "^10.2.1", 95 | "typescript": "^4.4.3", 96 | "webpack": "^5.50.0", 97 | "webpack-cli": "^4.7.2", 98 | "webpack-dev-server": "^3.11.2" 99 | }, 100 | "dependencies": { 101 | "concurrently": "^6.2.1", 102 | "npm": "^7.24.0", 103 | "react": "^17.0.2", 104 | "react-dom": "^17.0.2" 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const tailwindcss = require('tailwindcss'); 2 | module.exports = { 3 | plugins: [ 4 | tailwindcss('./tailwind.config.js'), 5 | require('autoprefixer'), 6 | ], 7 | // plugins: { 8 | // '@tailwindcss/jit': {}, 9 | // autoprefixer: {}, 10 | // }, 11 | }; -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

FaradayJS- electron security.

5 |

6 | GitHub license 7 | GitHub stars 8 | GitHub issues 9 | GitHub last commit 10 | 11 |

12 |

Table of Contents

13 | 14 | - [About](https://github.com/oslabs-beta/FaradayJS/#about-faradayjs) 15 | - [Features](https://github.com/oslabs-beta/FaradayJS/#Features) 16 | - [Download FaradayJS](https://github.com/oslabs-beta/FaradayJS/#download-faradayjs) 17 | - [How to Use](https://github.com/oslabs-beta/FaradayJS/#how-to-use) 18 | - [Team](https://github.com/oslabs-beta/FaradayJS/#our-team) 19 | - [License](https://github.com/oslabs-beta/FaradayJS/#License) 20 | - [Languages & Tools](https://github.com/oslabs-beta/FaradayJS/#languages-and-tools) 21 | 22 |

About FaradayJS

23 | 24 | Similar to how the Faraday cage blocks electromagnetic fileds, FaradayJS protects electron developers from settings which would make their applications vulnerable. 25 | 26 |

Features

27 | 28 |

Download FaradayJS

29 | 30 | FaradayJS can be downloaded for Windows and Mac at our repo's releases page 31 | 32 | 33 |

How to Use

34 |

35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |

44 | 45 |

Our Team

46 | 47 | The team at FaradayJS 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |

Kelsey Flynn

Rosio Reyes

Heeho Kang

Miklós Kertész

kelseyGitHub rosioGitHub heehoGitHub miklosGitHub
69 | 70 | 71 | 72 |

License

73 | 74 | This product is licensed under the MIT License. 75 | 76 | This open source product is accelerated by [OS Labs](https://opensourcelabs.io/). 77 | 78 | 79 |

Languages and Tools:

80 |

81 | javascript 82 | typescript 83 | react 84 | redux 85 | tailwind 86 | nodejs 87 | electron 88 | webpack 89 | mocha 90 | chai 91 | spectron 92 |

93 | -------------------------------------------------------------------------------- /src/appUtil/checker.ts: -------------------------------------------------------------------------------- 1 | import defaultConfig from './defaultConfig' 2 | import settingsInfo from './securitySettingsInfo'; 3 | import groupSettings from './groupSettings'; 4 | // console.log("settings: ", settingsInfo); 5 | 6 | const checker = (propertiesObj: { [key: string]: any }, version: number) => { 7 | const versionDefaults: any = groupSettings(version) 8 | const testResults: any = []; 9 | 10 | for (let test in versionDefaults) { 11 | 12 | if (versionDefaults[test].hasOwnProperty('failValue')) { 13 | const testProp: any = test; 14 | const testFailValue: any = versionDefaults[test].failValue; 15 | const testResult = { 16 | testProp: testProp, 17 | failValue: testFailValue, 18 | status: 'unknown', 19 | description: 'none', 20 | start: 0, 21 | end: 0 22 | }; 23 | // Check if the properties that were grabbed from the users codebase includes this 24 | // setting that was in our default configuration object 25 | if (propertiesObj.hasOwnProperty(testProp)) { 26 | // it was in that object 27 | //identify the lines for start and end 28 | testResult.start = propertiesObj[testProp].start; 29 | testResult.end = propertiesObj[testProp].end; 30 | 31 | // if the value of that setting is equal to the testFail value 32 | // give it a fail status and description 33 | if (propertiesObj[testProp].value === testFailValue) { 34 | testResult.description = versionDefaults[testProp].description; 35 | testResult.status = 'fail'; 36 | } else if (propertiesObj[testProp].value === !testFailValue) { 37 | // else if is not equal to the testFail value 38 | // give it a pass status and description 39 | testResult.description = versionDefaults[testProp].description; 40 | testResult.status = 'pass'; 41 | } 42 | } else if (versionDefaults[testProp].default == testFailValue) { 43 | // Case that the setting was not in the files 44 | // Check if the default value for the version # is equal to the testFail value 45 | // if so, assign it a fail by default value and description 46 | testResult.status = 'fail by default'; 47 | testResult.description = versionDefaults[testProp].description; 48 | } else { //if (versionDefaults[testProp] === !failValue) { 49 | // Case assumes that the setting was not in the files and that 50 | // the default value is not equal to the testFail value 51 | // assign it a pass by default and description 52 | testResult.status = 'pass by default'; 53 | testResult.description = versionDefaults[testProp].description; 54 | // continue; 55 | } 56 | testResults.push(testResult); 57 | } 58 | } 59 | return testResults; 60 | }; 61 | 62 | 63 | export default checker -------------------------------------------------------------------------------- /src/appUtil/defaultConfig.ts: -------------------------------------------------------------------------------- 1 | const defaultConfig = { 2 | // Default settings by version 3 | "0.1.0": { 4 | "allowRunningInsecureContent": { 5 | "default": false 6 | }, 7 | "contextIsolation": { 8 | "default": false 9 | }, 10 | "enableRemoteModule": { 11 | "default": true 12 | }, 13 | "experimentalFeatures": { 14 | "default": false 15 | }, 16 | /// 17 | "images": { 18 | "default": true 19 | }, 20 | //*** TO DO */ 21 | "javascript": { 22 | "default": true 23 | }, 24 | //*** TO DO */ 25 | "nativeWindowOpen": { 26 | "default": false 27 | }, 28 | //*** TO DO */ 29 | "navigateOnDragDrop": { 30 | "default": false 31 | }, 32 | //*** TO DO */ 33 | "nodeIntegration": { 34 | "default": true 35 | }, 36 | "nodeIntegrationInWorker": { 37 | "default": false 38 | }, 39 | //*** TO DO */ 40 | "offscreen": { 41 | "default": false 42 | }, 43 | "plugins": { 44 | "default": false 45 | }, 46 | //*** TO DO */ 47 | "safeDialogs": { 48 | "default": false 49 | }, 50 | "sandbox": { 51 | "default": false 52 | }, 53 | /// 54 | "spellcheck": { 55 | "default": true 56 | }, 57 | /// 58 | "textAreasAreResizable": { 59 | "default": true 60 | }, 61 | "webSecurity": { 62 | "default": true 63 | }, 64 | "webgl": { 65 | "default": true 66 | }, 67 | "webviewTag": { 68 | "default": true 69 | } 70 | }, 71 | "5.0.0": { 72 | "nodeIntegrationInSubFrames": { 73 | "default": false 74 | }, 75 | "allowRunningInsecureContent": { 76 | "default": false 77 | }, 78 | "contextIsolation": { 79 | "default": false 80 | }, 81 | "enableRemoteModule": { 82 | "default": true 83 | }, 84 | "experimentalFeatures": { 85 | "default": false 86 | }, 87 | "images": { 88 | "default": true 89 | }, 90 | "javascript": { 91 | "default": true 92 | }, 93 | "nativeWindowOpen": { 94 | "default": false 95 | }, 96 | "navigateOnDragDrop": { 97 | "default": false 98 | }, 99 | "nodeIntegration": { 100 | "default": false 101 | }, 102 | "nodeIntegrationInWorker": { 103 | "default": false 104 | }, 105 | "offscreen": { 106 | "default": false 107 | }, 108 | "plugins": { 109 | "default": false 110 | }, 111 | "safeDialogs": { 112 | "default": false 113 | }, 114 | "sandbox": { 115 | "default": false 116 | }, 117 | "spellcheck": { 118 | "default": true 119 | }, 120 | "textAreasAreResizable": { 121 | "default": true 122 | }, 123 | "webSecurity": { 124 | "default": true 125 | }, 126 | "webgl": { 127 | "default": true 128 | }, 129 | "webviewTag": { 130 | "default": false 131 | } 132 | }, 133 | "10.0.0": { 134 | "allowRunningInsecureContent": { 135 | "default": false 136 | }, 137 | "contextIsolation": { 138 | "default": false 139 | }, 140 | "enableRemoteModule": { 141 | "default": false 142 | }, 143 | "enableWebSQL": { 144 | "default": true 145 | }, 146 | "experimentalFeatures": { 147 | "default": false 148 | }, 149 | "images": { 150 | "default": true 151 | }, 152 | "javascript": { 153 | "default": true 154 | }, 155 | "nativeWindowOpen": { 156 | "default": false 157 | }, 158 | "navigateOnDragDrop": { 159 | "default": false 160 | }, 161 | "nodeIntegration": { 162 | "default": false 163 | }, 164 | "nodeIntegrationInSubFrames": { 165 | "default": false 166 | }, 167 | "nodeIntegrationInWorker": { 168 | "default": false 169 | }, 170 | "offscreen": { 171 | "default": false 172 | }, 173 | "plugins": { 174 | "default": false 175 | }, 176 | "safeDialogs": { 177 | "default": false 178 | }, 179 | "sandbox": { 180 | "default": false 181 | }, 182 | "spellcheck": { 183 | "default": true 184 | }, 185 | "textAreasAreResizable": { 186 | "default": true 187 | }, 188 | "webSecurity": { 189 | "default": true 190 | }, 191 | "webgl": { 192 | "default": true 193 | }, 194 | "webviewTag": { 195 | "default": false 196 | } 197 | }, 198 | "12.0.0": { 199 | "allowRunningInsecureContent": { 200 | "default": false 201 | }, 202 | "contextIsolation": { 203 | "default": true 204 | }, 205 | "enableRemoteModule": { 206 | "default": false 207 | }, 208 | "enableWebSQL": { 209 | "default": true 210 | }, 211 | "experimentalFeatures": { 212 | "default": false 213 | }, 214 | "images": { 215 | "default": true 216 | }, 217 | "javascript": { 218 | "default": true 219 | }, 220 | "nativeWindowOpen": { 221 | "default": false 222 | }, 223 | "navigateOnDragDrop": { 224 | "default": false 225 | }, 226 | "nodeIntegration": { 227 | "default": false 228 | }, 229 | "nodeIntegrationInSubFrames": { 230 | "default": false 231 | }, 232 | "nodeIntegrationInWorker": { 233 | "default": false 234 | }, 235 | "offscreen": { 236 | "default": false 237 | }, 238 | "plugins": { 239 | "default": false 240 | }, 241 | "safeDialogs": { 242 | "default": false 243 | }, 244 | "sandbox": { 245 | "default": false 246 | }, 247 | "spellcheck": { 248 | "default": true 249 | }, 250 | "textAreasAreResizable": { 251 | "default": true 252 | }, 253 | "webSecurity": { 254 | "default": true 255 | }, 256 | "webgl": { 257 | "default": true 258 | }, 259 | "webviewTag": { 260 | "default": false 261 | } 262 | }, 263 | "13.0.0": { 264 | "allowRunningInsecureContent": { 265 | "default": false 266 | }, 267 | "contextIsolation": { 268 | "default": true 269 | }, 270 | "enableRemoteModule": { 271 | "default": false 272 | }, 273 | "enableWebSQL": { 274 | "default": true 275 | }, 276 | "experimentalFeatures": { 277 | "default": false 278 | }, 279 | "images": { 280 | "default": true 281 | }, 282 | "javascript": { 283 | "default": true 284 | }, 285 | "nativeWindowOpen": { 286 | "default": false 287 | }, 288 | "navigateOnDragDrop": { 289 | "default": false 290 | }, 291 | "nodeIntegration": { 292 | "default": false 293 | }, 294 | "nodeIntegrationInSubFrames": { 295 | "default": false 296 | }, 297 | "nodeIntegrationInWorker": { 298 | "default": false 299 | }, 300 | "offscreen": { 301 | "default": false 302 | }, 303 | "plugins": { 304 | "default": false 305 | }, 306 | "safeDialogs": { 307 | "default": false 308 | }, 309 | "sandbox": { 310 | "default": false 311 | }, 312 | "spellcheck": { 313 | "default": true 314 | }, 315 | "textAreasAreResizable": { 316 | "default": true 317 | }, 318 | "webSecurity": { 319 | "default": true 320 | }, 321 | "webgl": { 322 | "default": true 323 | }, 324 | "webviewTag": { 325 | "default": false 326 | } 327 | }, 328 | "15.0.0": { 329 | "allowRunningInsecureContent": { 330 | "default": false 331 | }, 332 | "contextIsolation": { 333 | "default": true 334 | }, 335 | "enableRemoteModule": { 336 | "default": false 337 | }, 338 | "enableWebSQL": { 339 | "default": true 340 | }, 341 | "experimentalFeatures": { 342 | "default": false 343 | }, 344 | "images": { 345 | "default": true 346 | }, 347 | "javascript": { 348 | "default": true 349 | }, 350 | "nativeWindowOpen": { 351 | "default": true 352 | }, 353 | "navigateOnDragDrop": { 354 | "default": false 355 | }, 356 | "nodeIntegration": { 357 | "default": false 358 | }, 359 | "nodeIntegrationInSubFrames": { 360 | "default": false 361 | }, 362 | "nodeIntegrationInWorker": { 363 | "default": false 364 | }, 365 | "offscreen": { 366 | "default": false 367 | }, 368 | "plugins": { 369 | "default": false 370 | }, 371 | "safeDialogs": { 372 | "default": false 373 | }, 374 | // should not add this CLI tag: --no-sandbox that's a testing feature only not for prod code 375 | // https://www.electronjs.org/docs/tutorial/sandbox 376 | "sandbox": { 377 | "default": false 378 | }, 379 | "spellcheck": { 380 | "default": true 381 | }, 382 | "textAreasAreResizable": { 383 | "default": true 384 | }, 385 | "webSecurity": { 386 | "default": true 387 | }, 388 | "webgl": { 389 | "default": true 390 | }, 391 | "webviewTag": { 392 | "default": false 393 | } 394 | } 395 | } 396 | 397 | export default defaultConfig -------------------------------------------------------------------------------- /src/appUtil/groupSettings.ts: -------------------------------------------------------------------------------- 1 | import defaultConfig from './defaultConfig' 2 | import settingsInfo from './securitySettingsInfo'; 3 | 4 | const groupSettings = (version: number) => { 5 | let userVersionNumber: number = parseInt(version.toString().split(".")[0]); 6 | console.log('version from user: ', userVersionNumber); 7 | const testsObj: any = {}; 8 | let versionObj: any = {}; 9 | Object.keys(defaultConfig).forEach((str, index) => { 10 | versionObj[parseInt(str.split(".")[0])] = index; 11 | }); 12 | 13 | let versionDefaults: any; 14 | 15 | if(userVersionNumber >= 0 && userVersionNumber < 5){ 16 | userVersionNumber = 0; 17 | }else if(userVersionNumber>= 5 && userVersionNumber < 10){ 18 | userVersionNumber = 5; 19 | }else if(userVersionNumber >= 10 && userVersionNumber < 12){ 20 | userVersionNumber = 10; 21 | }else if(userVersionNumber >= 13 && userVersionNumber < 15){ 22 | userVersionNumber = 13; 23 | }else if(userVersionNumber >= 15){ 24 | userVersionNumber = 15; 25 | }else { 26 | console.log('Using default version 13.0.0'); 27 | userVersionNumber = 13; 28 | } 29 | versionDefaults = Object.values(defaultConfig)[versionObj[userVersionNumber]]; 30 | 31 | for(const [setting, values] of Object.entries(versionDefaults)){ 32 | if(settingsInfo[setting]){ 33 | versionDefaults[setting].failValue = settingsInfo[setting].failValue; 34 | versionDefaults[setting].description = settingsInfo[setting].description; 35 | } 36 | } 37 | // add a test for the version, if it is older than version 13 38 | if(userVersionNumber < 13){ 39 | console.log('Version needs to be updated'); 40 | versionDefaults["needToUpdateVersion"] = { 41 | default: true, 42 | failValue: true, 43 | description: "It is recommended that you update to the latest version of Electron as Electron is continuiously implementing updates and changes that make your application more secure." 44 | }; 45 | } 46 | 47 | return versionDefaults; 48 | } 49 | 50 | export default groupSettings -------------------------------------------------------------------------------- /src/appUtil/ignoreArr.ts: -------------------------------------------------------------------------------- 1 | export const doInclude = ['.js', '.jsx', '.ts', '.tsx', '.html']; 2 | export const doNotInclude = ['.vscode', '.json', 'node_modules', '.txt', 'dist', 'build']; 3 | 4 | -------------------------------------------------------------------------------- /src/appUtil/menuTemplate.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { app, BrowserWindow, ipcMain, dialog, Menu } from 'electron'; 3 | const isMac = process.platform === 'darwin'; 4 | 5 | const menuTemplate = (win) => { 6 | return [ 7 | { 8 | label: 'File', 9 | submenu: [ 10 | { 11 | label: 'Open File', 12 | accelerator: 'CmdOrCtrl+O', 13 | click: () => { 14 | console.log('opened file'); 15 | }, 16 | }, 17 | { label: 'Open Folder' }, 18 | isMac ? { role: 'close' } : { role: 'quit' }, 19 | { type: 'separator' }, 20 | { 21 | label: 'test-1', 22 | submenu: [{ label: 'test-2' }], 23 | }, 24 | ], 25 | }, 26 | ...(isMac 27 | ? [ 28 | { 29 | label: app.name, 30 | submenu: [ 31 | { role: 'about' }, 32 | { type: 'separator' }, 33 | { role: 'services' }, 34 | { type: 'separator' }, 35 | { role: 'hide' }, 36 | { role: 'hideothers' }, 37 | { role: 'unhide' }, 38 | { type: 'separator' }, 39 | { role: 'quit' }, 40 | ], 41 | }, 42 | ] 43 | : []), 44 | // { role: 'editMenu' } 45 | { 46 | label: 'Edit', 47 | submenu: [ 48 | { role: 'undo' }, 49 | { role: 'redo' }, 50 | { type: 'separator' }, 51 | { role: 'cut' }, 52 | { role: 'copy' }, 53 | { role: 'paste' }, 54 | ...(isMac 55 | ? [ 56 | { role: 'pasteAndMatchStyle' }, 57 | { role: 'delete' }, 58 | { role: 'selectAll' }, 59 | { type: 'separator' }, 60 | { 61 | label: 'Speech', 62 | submenu: [{ role: 'startSpeaking' }, { role: 'stopSpeaking' }], 63 | }, 64 | ] 65 | : [{ role: 'delete' }, { type: 'separator' }, { role: 'selectAll' }]), 66 | ], 67 | }, 68 | // { role: 'viewMenu' } 69 | { 70 | label: 'View', 71 | submenu: [ 72 | { role: 'reload' }, 73 | { role: 'forceReload' }, 74 | { role: 'toggleDevTools' }, 75 | { type: 'separator' }, 76 | { role: 'resetZoom' }, 77 | { role: 'zoomIn' }, 78 | { role: 'zoomOut' }, 79 | { type: 'separator' }, 80 | { role: 'togglefullscreen' }, 81 | ], 82 | }, 83 | // { role: 'windowMenu' } 84 | { 85 | label: 'Window', 86 | submenu: [ 87 | { role: 'minimize' }, 88 | { role: 'zoom' }, 89 | ...(isMac 90 | ? [ 91 | { type: 'separator' }, 92 | { role: 'front' }, 93 | { type: 'separator' }, 94 | { role: 'window' }, 95 | ] 96 | : [{ role: 'close' }]), 97 | ], 98 | }, 99 | { 100 | role: 'help', 101 | submenu: [ 102 | { 103 | label: 'Learn More', 104 | click: async () => { 105 | const { shell } = require('electron'); 106 | await shell.openExternal('https://electronjs.org'); 107 | }, 108 | }, 109 | ], 110 | }, 111 | { 112 | label: 'Developer', 113 | submenu: [ 114 | { 115 | label: 'Toggle Developer Tools', 116 | accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I', 117 | click: () => { 118 | win.webContents.toggleDevTools(); 119 | }, 120 | }, 121 | ], 122 | }, 123 | ]; 124 | }; 125 | 126 | export default menuTemplate; 127 | -------------------------------------------------------------------------------- /src/appUtil/openFolder.ts: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | import { dialog } from 'electron'; 3 | 4 | const OpenFolder = async (win:any, doInclude:string[], doNotInclude:string[]) => { 5 | try { 6 | const folders: Promise | Boolean | String = dialog.showOpenDialog(win, { 7 | properties: ['openDirectory'] 8 | }); 9 | 10 | const folder = await folders; // // returns {canceled: false, filePaths: [ 'D:\\Codesmith\\Projects\\TestElectron' ]} 11 | 12 | const returnValue: any = {}; 13 | returnValue.fileObjectArray = []; 14 | returnValue.packageJsonContents = ''; 15 | 16 | const readAllFolder = async (dirMain: string) => { 17 | 18 | const readDirMain = fs.readdirSync(dirMain); 19 | 20 | readDirMain.forEach(async (dirNext: string) => { 21 | const nextDirectory = dirMain + "/" + dirNext; 22 | if (fs.lstatSync(nextDirectory).isDirectory()) { 23 | readAllFolder(nextDirectory); 24 | } else { 25 | if (doInclude.some(el=>nextDirectory.includes(el)) && !doNotInclude.some(el=>nextDirectory.includes(el))){ 26 | const fileContent = fs.readFileSync(nextDirectory).toString(); 27 | const fileObj: any = { 28 | path: dirMain + '/', //process.platform === 'darwin' ? '/' : '\\', 29 | fileName: dirNext, 30 | contents: fileContent 31 | } 32 | returnValue.fileObjectArray.push(fileObj); 33 | } else if (nextDirectory.includes('package.json')){ 34 | returnValue.packageJsonContents = await fs.readFileSync(nextDirectory).toString(); 35 | } 36 | } 37 | }); 38 | return returnValue; 39 | } 40 | 41 | if(folder.canceled){ 42 | return 43 | }else{ 44 | return await readAllFolder(folder.filePaths[0]); 45 | } 46 | 47 | } catch(err){ 48 | console.log('Open Folder Error: ', err); 49 | } 50 | } 51 | 52 | export default OpenFolder -------------------------------------------------------------------------------- /src/appUtil/parser.ts: -------------------------------------------------------------------------------- 1 | import * as parse from '@typescript-eslint/typescript-estree' 2 | const htmlparser2 = require('htmlparser2'); 3 | 4 | export const parser = async (obj: string) => { 5 | try { 6 | const options = { 7 | jsx: true, 8 | loc: true, 9 | }; 10 | const parsed = await parse.parse(obj, options); 11 | return parsed; 12 | } catch (e) { 13 | console.log('Parse Error: ', e); 14 | } 15 | }; 16 | 17 | export const htmlparser = (obj: string) => { 18 | //const dom = htmlparser2.parseDocument(obj); // you can see the whole DOM 19 | const tests: any = { 20 | disablewebsecurity: { 21 | default: false, 22 | description: "Related to webSecurity. This allows the execution of insecure code from different domains. It allows CORS requests (an origin is considered the same origin if it has the same protocol, port (if specified) and host), and ignores X-Frame-Options which serve to indicate whether or not, and in what context a browser is allowed to render a page. Enabling this setting disables the same-origin policy and sets allowRunningInsecureContent to true.", 23 | failValue: true, 24 | }, 25 | allowpopups: { 26 | default: false, 27 | description: "New windows will open a BrowserWindow using window.open() when nativeWindowOpen is set to true and create a BrowserWindowProxy when nativeWindowOpen is set to false. BrowserWindow by default has complete access to the Node API. Allowing popups and loading insecure content in your application can pose security risks. Node Integration and Javascript will be disabled in the new window if it is also disabled in the parent window. Similarly, context isolation will be enabled if it is enabled in the parent window. It is best to only allow websites to create new popups if you are sure it is necessary. ", 28 | failValue: true 29 | } 30 | } 31 | const testResults: any = []; 32 | 33 | const parsed = new htmlparser2.Parser({ 34 | onopentag( 35 | name: string, 36 | attribute: { [key: string]: string | number | boolean | undefined | null } 37 | ) { 38 | for (let test in tests) { 39 | const testResult = { 40 | testProp: test, 41 | failValue: tests[test].failValue, 42 | description: "none", 43 | status: 'unknown', 44 | start: 0, 45 | end: 0 46 | }; 47 | if (tests[test].hasOwnProperty('failValue')) { 48 | if (name === 'webview' && 49 | attribute.hasOwnProperty(test) 50 | ) { 51 | testResult.status = 'fail'; 52 | testResult.description = tests[test].description; 53 | console.log('testResult.description: ', testResult.description); 54 | testResults.push(testResult); 55 | } else if (name === 'webview' && 56 | !attribute.hasOwnProperty(test) 57 | ) { 58 | testResult.status = 'pass by default'; 59 | testResult.description = tests[test].description; 60 | console.log('testResult.description: ', testResult.description); 61 | testResults.push(testResult); 62 | } 63 | } 64 | } 65 | }, 66 | ontext(text: string) {}, 67 | onclosetag(tagname: string) {}, 68 | }); 69 | 70 | parsed.write(obj); 71 | 72 | parsed.end(); 73 | 74 | // loop through keys in tests obj 75 | // checking if that key does NOT exist in testResults 76 | // then push to testResults a testResult obj defined here that has a status of 'pass by default' 77 | 78 | 79 | for (let test in tests) { 80 | // const isResultPresent = testResults.reduce((accum: boolean, currVal: any) => { 81 | // if (currVal.testProp === test) return accum = true; 82 | // }, false); 83 | let isResultPresent: boolean = false; 84 | 85 | testResults.forEach((obj:any) =>{ 86 | if(obj.testProp === test){ 87 | isResultPresent = true 88 | } 89 | }) 90 | 91 | if(!isResultPresent) { 92 | testResults.push({ 93 | testProp: test, 94 | failValue: tests[test].failValue, 95 | description: tests[test].description, 96 | status: 'pass by default', 97 | start: 0, 98 | end: 0 99 | }); 100 | } 101 | } 102 | 103 | return testResults; 104 | }; 105 | -------------------------------------------------------------------------------- /src/appUtil/securitySettingsInfo.ts: -------------------------------------------------------------------------------- 1 | const settingsInfo : { [key: string]: any } = { 2 | // https://www.electronjs.org/docs/api/browser-window 3 | 4 | // *************************************************************************** 5 | // Found in HTML 6 | "allowpopups": { 7 | "failValue": true, 8 | "description": "New windows will open a BrowserWindow using window.open() when nativeWindowOpen is set to true and create a BrowserWindowProxy when nativeWindowOpen is set to false. BrowserWindow by default has complete access to the Node API. Allowing popups and loading insecure content in your application can pose security risks. Node Integration and Javascript will be disabled in the new window if it is also disabled in the parent window. Similarly, context isolation will be enabled if it is enabled in the parent window. It is best to only allow websites to create new popups if you are sure it is necessary. " 9 | }, 10 | "disablewebsecurity": { 11 | "failValue": true, 12 | "description": "Related to webSecurity. This allows the execution of insecure code from different domains. It allows CORS requests (an origin is considered the same origin if it has the same protocol, port (if specified) and host), and ignores X-Frame-Options which serve to indicate whether or not, and in what context a browser is allowed to render a page. Enabling this setting disables the same-origin policy and sets allowRunningInsecureContent to true." 13 | }, 14 | // *************************************************************************** 15 | // Found in JS/TS Files 16 | "webSecurity": { 17 | "failValue": false, 18 | "description": "Related to disablewebsecurity. This allows the execution of insecure code from different domains. It allows CORS requests (an origin is considered the same origin if it has the same protocol, port (if specified) and host), and ignores X-Frame-Options which serve to indicate whether or not, and in what context a browser is allowed to render a page. Enabling this setting disables the same-origin policy and sets allowRunningInsecureContent to true." 19 | }, 20 | // https://slack.engineering/the-app-sandbox/ 21 | "sandbox": { 22 | // best to enable 23 | "failValue": null, 24 | "description": "Sandboxing is a Chromium feature that uses the operating system to significantly limit what renderer processes have access to, which essentially becomes only the ability to send messages to the main process. Enabling this feature means that your renderer process cannot use Node or any external module that depends on any of the core modules of Node. It is a feature used to mitigate the many potential security risks we cannot account for. " 25 | }, 26 | "allowRunningInsecureContent": { 27 | "failValue": true, 28 | "description": "Setting this to true disables the default electron settings that prohibit websites loaded over secure sources (HTTPS), to load and execute scripts, CSS, or plugins from insecure sources (HTTP). Depending on the content you load onto your page, this could pose varying levels of security risks." 29 | }, 30 | "enableBlinkFeatures": { 31 | // Can have many different values. Property is ideally not declared in files unless there's a clear purpose 32 | // If this is declared in application then it fails this test, regardless of what it's set to 33 | "failValue": true, 34 | "description": "Blink is Chromium's rendering engine and setting this property to true enables features which had been previously disabled, by default, for security purposes. Some blink feratures are the following: KeyboardEventKey and ExecCommandInJavaScript. You should fully understand the security risks of the features you are enanbling and try your best to safeguard your application." 35 | }, 36 | "experimentalFeatures": { 37 | "failValue": true, 38 | "description": "The impact of experimentalFeatures on Electron applications has not been tested. If not strictly required do not enable." 39 | }, 40 | // ******************************************************************************************************** 41 | // TO RESEARCH 42 | 43 | // Enabled by default since Electron 12>= 44 | "contextIsolation": { 45 | "failValue": false, 46 | "description": "Ensures that the preload scripts and the internal logic of your app run in a separate context to the websites loaded in webContents." 47 | }, 48 | // Disabled by default since Electron 10>= 49 | "enableRemoteModule": { 50 | "failValue": true, 51 | "description": "Renders sandboxing your renderer useless. Renderer is only as secure as the main process keeps it in check." 52 | }, 53 | 54 | "images": { 55 | "failValue": null, 56 | "description": "none" 57 | }, 58 | "javascript": { 59 | "failValue": null, 60 | "description": "none" 61 | }, 62 | "nativeWindowOpen": { 63 | "failValue": null, 64 | "description": "When set to false window.open results in the creation of a BrowserWindowProxy wrapper around BrowserWindow. Electron pairs the native Chrome window with a BrowserWindow under the hood." 65 | }, 66 | "navigateOnDragDrop": { 67 | "failValue": null, 68 | "description": "none" 69 | }, 70 | "nodeIntegration": { 71 | "failValue": true, 72 | "description": "Enables/disables the use of NodeJS " 73 | }, 74 | "nodeIntegrationInWorker": { 75 | "failValue": true, 76 | "description": "none" 77 | }, 78 | "offscreen": { 79 | "failValue": null, 80 | "description": "none" 81 | }, 82 | "plugins": { 83 | "failValue": true, 84 | "description": "none" 85 | }, 86 | "safeDialogs": { 87 | "failValue": true, 88 | "description": "none" 89 | }, 90 | "spellcheck": { 91 | "failValue": null, 92 | "description": "none" 93 | }, 94 | "textAreasAreResizable": { 95 | "failValue": null, 96 | "description": "none" 97 | }, 98 | // https://security.stackexchange.com/questions/13799/is-webgl-a-security-concern 99 | // best if its disabled but its typically enabled by default, might be necessary 100 | "webgl": { 101 | "failValue": null, 102 | "description": "WebGL is a JS API used to render interactive graphics. It allows direct access to the GPU, so while it can be a potential security concern, most browsers ensure that running your code will not be a major security issue." 103 | }, 104 | // Disabled as of Version 5>= 105 | // 106 | // 107 | // can also set within this tag: plugins, preload, disablewebsecurity, allowpopups, webpreferences, partition, useragent 108 | // enableblinkfeatures, disableblinkfeatures 109 | // has methods: loadURL(), downloadURL(), getURL(), getTitle(), isLoading(), isLoadingMainFraim(), isWaitingForResponse(), 110 | // stop(), reload(), reloadIgnoringCache(),......... and MANY MORE 111 | // https://stackoverflow.com/questions/37602759/what-is-the-difference-between-browserwindow-and-webview-tag-in-electron-and-w 112 | // https://www.electronjs.org/docs/api/webview-tag 113 | "webviewTag": { 114 | "failValue": true, 115 | "description": "Used to embed guest content in your application, this tags runs in a separate process than your application and would normally be safe to use, but as it is based on Chromiums webview, which is undergoing major structural changes it is not recommended to use this tag at this time." 116 | }, 117 | "enableWebSQL": { 118 | "failValue": null, 119 | "description": "none" 120 | }, 121 | "nodeIntegrationInSubFrames": { 122 | "failValue": true, 123 | "description": "none" 124 | } 125 | // , 126 | // "needToUpdateVersion": { 127 | // "failValue": true, 128 | // "description": "It is recommended that you update to the latest version of Electron as Electron is continuiously implementing updates and changes that make your application more secure." 129 | // } 130 | 131 | } 132 | export default settingsInfo -------------------------------------------------------------------------------- /src/appUtil/traverse.ts: -------------------------------------------------------------------------------- 1 | const traverser = (node:any, level:number) =>{ 2 | let tempCache: {[key:string]:string|number|boolean|undefined|null} = {}; 3 | 4 | const traverse = (node:any, level:number) =>{ 5 | //const indent = Array(level+1).join(" "); 6 | //console.log(indent, "type:", node.type) 7 | 8 | switch(node.type){ 9 | case "Program": 10 | for(let child of node.body){ 11 | traverse(child, level+1); 12 | } 13 | break; 14 | case "ImportDeclaration": 15 | break; 16 | case "VariableDeclaration": 17 | for(let child of node.declarations){ 18 | traverse(child, level+1) 19 | } 20 | break; 21 | case "VariableDeclarator": 22 | if(node.id !== null){ 23 | traverse(node.id, level+1); 24 | } 25 | if(node.init !== null){ 26 | traverse(node.init, level+1) 27 | } 28 | break; 29 | case "ExportDefaultDeclaration": 30 | traverse(node.declaration, level+1); 31 | break; 32 | case "ClassDeclaration": 33 | traverse(node.body, level+1); 34 | break; 35 | case "ClassBody": 36 | for(let child of node.body){ 37 | traverse(child, level+1); 38 | } 39 | break; 40 | case "ClassProperty": 41 | traverse(node.key, level+1); 42 | traverse(node.value, level+1); 43 | break; 44 | case "Literal": 45 | //console.log(indent, node.value) 46 | break; 47 | case "Identifier": 48 | //console.log(indent, node.name) 49 | break; 50 | case "ArrowFunctionExpression": 51 | traverse(node.body, level+1); 52 | break; 53 | case "FunctionExpression": 54 | traverse(node.body, level+1); 55 | break; 56 | case "BinaryExpression": 57 | traverse(node.left, level+1); 58 | traverse(node.right, level+1); 59 | break; 60 | case "CallExpression": 61 | traverse(node.callee, level+1); 62 | for(let child of node.arguments){ 63 | traverse(child, level+1); 64 | } 65 | break; 66 | case "AwaitExpression": 67 | traverse(node.argument, level+1); 68 | break; 69 | case "MemberExpression": 70 | traverse(node.object, level+1); 71 | traverse(node.property, level+1); 72 | break; 73 | case "BlockStatement": 74 | for(let child of node.body){ 75 | traverse(child, level+1); 76 | } 77 | break; 78 | case "ExpressionStatement": 79 | traverse(node.expression, level+1) 80 | break; 81 | case "ForOfStatement": 82 | traverse(node.body, level+1) 83 | break; 84 | case "TryStatement": 85 | traverse(node.block, level+1) 86 | traverse(node.handler, level+1) 87 | break; 88 | case "ReturnStatement": 89 | traverse(node.argument, level+1) 90 | break; 91 | case "CatchClause": 92 | traverse(node.param, level+1) 93 | traverse(node.body, level+1) 94 | break; 95 | case "ArrayExpression": 96 | for(let child of node.elements){ 97 | traverse(child, level+1) 98 | } 99 | break; 100 | case "UnaryExpression": 101 | traverse(node.argument, level+1) 102 | break; 103 | case "AssignmentExpression": 104 | traverse(node.left, level+1); 105 | traverse(node.right, level+1); 106 | break; 107 | case "NewExpression": 108 | traverse(node.callee, level+1); 109 | for(let child of node.arguments){ 110 | traverse(child, level+1) 111 | } 112 | break; 113 | case "ObjectExpression": 114 | for(let child of node.properties){ 115 | traverse(child, level+1); 116 | } 117 | break; 118 | case "Property": 119 | traverse(node.key, level+1) 120 | traverse(node.value, level+1) 121 | tempCache[node.key.name] = node.value.value 122 | break; 123 | case "JSXElement": 124 | traverse(node.openingElement, level+1) 125 | traverse(node.closingElement, level+1) 126 | for(let child of node.children){ 127 | traverse(child, level+1) 128 | } 129 | break; 130 | case "JSXIdentifier": 131 | //console.log(node.name) 132 | break; 133 | case "IfStatement": 134 | break; 135 | case "TemplateLiteral": 136 | break; 137 | case "ObjectPattern": 138 | for(let child of node.properties){ 139 | traverse(child, level+1) 140 | } 141 | break; 142 | default: 143 | throw new Error("Node type not handled") 144 | } 145 | } 146 | //console.log(tempCache) 147 | traverse(node, level); 148 | return tempCache 149 | } 150 | 151 | 152 | export default traverser 153 | -------------------------------------------------------------------------------- /src/appUtil/tsestraverse.ts: -------------------------------------------------------------------------------- 1 | const estraverse = require('estraverse-jsx') 2 | 3 | const traverser = async (ast:any) =>{ 4 | try { 5 | let cache:{[key:string]: object} = {} 6 | 7 | await estraverse.traverse(ast, { 8 | enter:function(node:any, parent:any){ 9 | if(node.type=='VariableDeclaration'){ 10 | return estraverse.VisitorOption.skip; 11 | } 12 | }, 13 | leave: function (node:any, parent:any) { 14 | if (node.type == 'Property') { 15 | // console.log(node); 16 | cache[node.key.name] = { 17 | value: node.value.value, 18 | start: node.loc.start.line, 19 | end: node.loc.end.line, 20 | } 21 | } 22 | }, 23 | keys:{ 24 | 'ClassProperty': ['key', 'value'], 25 | 'TSModuleDeclaration': ['body'], 26 | 'TSModuleBlock': ['body'] 27 | }, 28 | fallback: 'iteration' 29 | }); 30 | return cache; 31 | } catch (e) { 32 | console.log('Traverse Error: ', e); 33 | } 34 | } 35 | 36 | export default traverser -------------------------------------------------------------------------------- /src/appUtil/tsmorph.ts: -------------------------------------------------------------------------------- 1 | import {Project, Node } from 'ts-morph' 2 | 3 | const tsmorph = async (path: string, property:string, value:any) =>{ 4 | const project = new Project(); 5 | const sourceFile = project.addSourceFileAtPath(path); 6 | 7 | sourceFile.forEachDescendant(node=>{ 8 | if(Node.isPropertyAssignment(node)){ 9 | if(node.getName() === `${property}`){ 10 | node.replaceWithText(`${property}: ${value}`) 11 | } 12 | } 13 | }) 14 | 15 | await sourceFile.save() 16 | await project.save() 17 | } 18 | 19 | export default tsmorph -------------------------------------------------------------------------------- /src/appUtil/versionFinder.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | const versionFinder = (obj:any) =>{ 4 | let version = 0; 5 | // Rather than using loops to locate electron version, I think hard-coding might be faster here, since this is O(1) 6 | 7 | if(obj.devDependencies && obj.devDependencies.electron){ 8 | let tempLiteral = obj.devDependencies.electron.replace('^', '').split(".") 9 | tempLiteral.pop(); 10 | version = Number(tempLiteral.join('.')) 11 | }else if (obj.dependencies && obj.dependencies.electron){ 12 | let tempLiteral = obj.dependencies.electron.replace('^', '').split(".") 13 | tempLiteral.pop(); 14 | version = Number(tempLiteral.join('.')) 15 | } 16 | return version; 17 | } 18 | 19 | export default versionFinder -------------------------------------------------------------------------------- /src/icons/Clipboard01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/FaradayJS/e28e2171d90103732cd5140b2d42b1fb38e48e9f/src/icons/Clipboard01.jpg -------------------------------------------------------------------------------- /src/icons/Clipboard02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/FaradayJS/e28e2171d90103732cd5140b2d42b1fb38e48e9f/src/icons/Clipboard02.jpg -------------------------------------------------------------------------------- /src/icons/Clipboard03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/FaradayJS/e28e2171d90103732cd5140b2d42b1fb38e48e9f/src/icons/Clipboard03.jpg -------------------------------------------------------------------------------- /src/icons/Clipboard04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/FaradayJS/e28e2171d90103732cd5140b2d42b1fb38e48e9f/src/icons/Clipboard04.jpg -------------------------------------------------------------------------------- /src/icons/GitHub-Mark-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/FaradayJS/e28e2171d90103732cd5140b2d42b1fb38e48e9f/src/icons/GitHub-Mark-64px.png -------------------------------------------------------------------------------- /src/icons/deleteIcon.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/icons/faraday-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/FaradayJS/e28e2171d90103732cd5140b2d42b1fb38e48e9f/src/icons/faraday-logo.png -------------------------------------------------------------------------------- /src/icons/google-docs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/openFolder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/icons/testing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { app, BrowserWindow, ipcMain, dialog, Menu } from 'electron'; 2 | import installExtension, { REDUX_DEVTOOLS, REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer'; 3 | import { parser, htmlparser } from './appUtil/parser' 4 | import checker from './appUtil/checker' 5 | import traverser from './appUtil/tsestraverse'; 6 | import versionFinder from './appUtil/versionFinder'; 7 | import menuTemplate from './appUtil/menuTemplate'; 8 | import tsmorph from './appUtil/tsmorph'; 9 | import OpenFolder from './appUtil/openFolder'; 10 | import {doInclude, doNotInclude} from './appUtil/ignoreArr' 11 | 12 | const fs = require('fs') 13 | const path = require('path') 14 | const isDev = require('electron-is-dev') 15 | 16 | let win: BrowserWindow; 17 | 18 | const options = { 19 | width: 800, 20 | height: 600, 21 | webPreferences: { 22 | nodeIntegration: false, 23 | preload: path.join(__dirname, "preload.js") 24 | } 25 | } 26 | 27 | const createWindow = (): void => { 28 | win = new BrowserWindow(options 29 | ); 30 | 31 | //Both methods work 32 | win.loadFile(path.join(__dirname, './index.html')); 33 | //win.loadURL(`file://${path.join(__dirname, "index.html")}`) 34 | 35 | 36 | ipcMain.on('main:open-file', async (event, payload)=>{ 37 | try{ 38 | const result = await OpenFile(); 39 | event.reply('preload:open-file', result) 40 | }catch(err){ 41 | console.log(err) 42 | } 43 | }) 44 | 45 | ipcMain.on('main:change-value', async(event, payload)=>{ 46 | tsmorph(payload[0]+payload[1], payload[2], payload[3]) 47 | }) 48 | 49 | ipcMain.on('main:open-folder', async (event, payload)=>{ 50 | try{ 51 | const rawResult: any = await OpenFolder(win, doInclude,doNotInclude); 52 | let processedResult; 53 | if(rawResult) processedResult = await processCodeBase(rawResult); 54 | event.sender.send('preload:open-folder', processedResult); 55 | } catch (e) { 56 | console.log('Open Folder Error: ', e); 57 | } 58 | }) 59 | 60 | ipcMain.on('main:refresh-code', async (event, payload)=>{ 61 | try{ 62 | let refreshedObj = await refreshCode(payload[0]+payload[1], payload[2]); 63 | event.sender.send('preload:refreshed-obj', refreshedObj) 64 | }catch(e){ 65 | console.log('Refresh Error ', e) 66 | } 67 | }) 68 | 69 | ipcMain.on('main:addIgnore', async(event, payload:string[])=>{ 70 | payload.forEach(x=>{ 71 | if(!doNotInclude.includes(x) && x !== undefined){ 72 | doNotInclude.push(x) 73 | } 74 | }) 75 | }) 76 | 77 | ipcMain.on('main:removeIgnore', async(event, payload:string)=>{ 78 | const index = doNotInclude.indexOf(payload); 79 | doNotInclude.splice(index, 1); 80 | }) 81 | 82 | 83 | const isMac = process.platform === 'darwin' 84 | const template: any = menuTemplate(win); 85 | const menu = Menu.buildFromTemplate(template); 86 | Menu.setApplicationMenu(menu); 87 | } 88 | 89 | app.on('ready', createWindow); 90 | 91 | // Open File Function 92 | const OpenFile = async () => { 93 | // Opens file dialog looking for markdown extension 94 | const files: Promise | Boolean | String = dialog.showOpenDialog(win, { 95 | properties: ['openFile', 'multiSelections'], 96 | }) 97 | 98 | // If no files 99 | if (!files) return; 100 | 101 | const file = await files; // Grabbing first item in the array. files(dialog.showOpenDialog) returns the absoulte path to the selected file 102 | if (file) { // !! ensures the resulting type is a boolean 103 | //console.log(file) 104 | }; 105 | 106 | } 107 | 108 | const reOrderTests = async (resultsArr: any) => { 109 | const reorderedTests: Array = []; 110 | for(let i = 0; i < resultsArr.length; i++){ 111 | if(resultsArr[i].fileResults["status"] == "fail" || resultsArr[i].fileResults["status"] == "fail by default") { 112 | reorderedTests.push(resultsArr[i]); 113 | } 114 | } 115 | for(let i = 0; i < resultsArr.length; i++){ 116 | if(resultsArr[i].fileResults["status"] == "pass" || resultsArr[i].fileResults["status"] == "pass by default") { 117 | reorderedTests.push(resultsArr[i]); 118 | } 119 | } 120 | return reorderedTests; 121 | } 122 | 123 | const processCodeBase = async (codebaseObj:any) => { 124 | try { 125 | let version = 13; 126 | if(codebaseObj.packageJsonContents){ 127 | version = await versionFinder(JSON.parse(codebaseObj.packageJsonContents)) 128 | } 129 | let rawTestResults: any[] = []; 130 | let traversedAstNodes: any = {}; 131 | 132 | const addFileNamesToResultsArray: any = (resultsArray: any, file: number) => { 133 | resultsArray.forEach((resultObj: any) => { 134 | rawTestResults.push({ 135 | fileResults: resultObj, 136 | fileName: codebaseObj.fileObjectArray[file].fileName, 137 | filePath: codebaseObj.fileObjectArray[file].path 138 | }); 139 | }); 140 | } 141 | 142 | for (let i = 0; i < codebaseObj.fileObjectArray.length; i++) { 143 | if(codebaseObj.fileObjectArray[i].fileName.includes('.html')){ 144 | const htmlFileResultsArray: any = await htmlparser(codebaseObj.fileObjectArray[i].contents); 145 | addFileNamesToResultsArray(htmlFileResultsArray, i); 146 | }else{ 147 | const ast: any = await parser(codebaseObj.fileObjectArray[i].contents); 148 | traversedAstNodes = await traverser(ast); 149 | if(traversedAstNodes.hasOwnProperty('webPreferences')){ 150 | const fileResultsArray: any = checker(traversedAstNodes, version); 151 | addFileNamesToResultsArray(fileResultsArray, i); 152 | } 153 | } 154 | } 155 | 156 | const reorderedTests: any = await reOrderTests(rawTestResults); 157 | return reorderedTests; 158 | }catch(err){ 159 | console.log('ProcessCodeBase: ', err) 160 | } 161 | } 162 | 163 | const refreshCode = async (path:string, passedTestProp:string) => { 164 | let version = 13; 165 | const fileContent = await fs.readFileSync(path).toString(); 166 | const ast:any = await parser(fileContent); 167 | let traversedAstNodes = await traverser(ast); 168 | const fileResultsArray: any = await checker([traversedAstNodes], version); 169 | for(let i = 0; i{ 7 | ipcRenderer.send('main:open-file') 8 | }, 9 | openFolder: ()=>{ 10 | ipcRenderer.send('main:open-folder') 11 | }, 12 | receiveData: (channel:any, func:any) =>{ 13 | let validChannels = ['preload:open-folder', 'preload:refreshed-obj']; 14 | if(validChannels.includes(channel)){ 15 | ipcRenderer.once(channel, (event, ...args)=>func(...args)) 16 | } 17 | }, 18 | changeValue: (args:any) =>{ 19 | ipcRenderer.send('main:change-value', args) 20 | }, 21 | refreshCode: (args:any) =>{ 22 | ipcRenderer.send('main:refresh-code', args) 23 | }, 24 | addIgnore:(args:any)=>{ 25 | ipcRenderer.send('main:addIgnore', args) 26 | }, 27 | removeIgnore:(args:string)=>{ 28 | ipcRenderer.send('main:removeIgnore', args) 29 | } 30 | } 31 | ) 32 | -------------------------------------------------------------------------------- /src/renderer/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import '../../src/styles.css'; 3 | import NavBar from './components/NavBar'; 4 | 5 | const App = () => { 6 | 7 | return ( 8 |
9 | 10 |
11 | ) 12 | } 13 | 14 | export default App; -------------------------------------------------------------------------------- /src/renderer/Slices/ignoreItemsSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import {TestResultState} from './testResultSlice' 3 | 4 | interface Ostate{ 5 | testresult: TestResultState, 6 | ignoreItems: IState 7 | } 8 | 9 | interface IState{ 10 | textInput:string, 11 | ignoreArr: string[] 12 | } 13 | 14 | interface addIgnoreState{ 15 | payload: string; 16 | type: string; 17 | } 18 | 19 | 20 | export const ignoreItems = createSlice({ 21 | name: 'ignoreItems', 22 | initialState:{ 23 | textInput: '', 24 | ignoreArr: [] 25 | }, 26 | reducers:{ 27 | setReduxTextInput: (state, action) =>{ 28 | state.textInput = action.payload; 29 | }, 30 | removeReduxTextInput: (state)=>{ 31 | state.textInput = ''; 32 | }, 33 | addIgnoreArr: (state:IState, action:addIgnoreState) =>{ 34 | state.ignoreArr = [...state.ignoreArr, action.payload] 35 | }, 36 | removeIgnoreArr: (state:IState, action:addIgnoreState)=>{ 37 | let tempArr = [...state.ignoreArr]; 38 | const index = tempArr.indexOf(action.payload); 39 | tempArr.splice(index, 1); // Remove 1 element at index 40 | state.ignoreArr = tempArr; 41 | } 42 | } 43 | 44 | }) 45 | 46 | export const { setReduxTextInput, removeReduxTextInput, addIgnoreArr, removeIgnoreArr } = ignoreItems.actions; 47 | export default ignoreItems.reducer; 48 | export const ignoreItemsState = (state:Ostate) => state.ignoreItems; -------------------------------------------------------------------------------- /src/renderer/Slices/loadingSlice.tsx: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | const initialState={gettingData: true} 4 | 5 | 6 | const loadingSlice = createSlice({ 7 | name: 'loading', 8 | initialState, 9 | reducers: { 10 | 11 | updateLoading(state){ 12 | state.gettingData=!state.gettingData 13 | }, 14 | }, 15 | }); 16 | 17 | export const { updateLoading} = loadingSlice.actions; 18 | export default loadingSlice.reducer; -------------------------------------------------------------------------------- /src/renderer/Slices/testResultSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | import {updateLoading} from'./loadingSlice' 3 | 4 | interface fileResult { 5 | start: number 6 | status: string 7 | end: number 8 | testProp: string 9 | description: string 10 | failValue: boolean 11 | } 12 | 13 | interface testResult { 14 | fileName: string, 15 | filePath: string, 16 | fileResults: fileResult 17 | } 18 | 19 | export interface TestResultState { 20 | projectName: string, 21 | testResults: testResult[], 22 | expansionStatus: boolean[], 23 | fixedStatus: boolean[], 24 | morphBools: boolean[], 25 | simpleBool: boolean, 26 | } 27 | 28 | const initialState = { 29 | projectName: '', 30 | testResults: [], 31 | expansionStatus: [], 32 | fixedStatus: [], 33 | morphBools: [], 34 | simpleBool: false, 35 | } as TestResultState; 36 | 37 | const testResultSlice = createSlice({ 38 | name: 'testresult', 39 | initialState, 40 | reducers: { 41 | // resetResults(state){state.testResults=[]}, 42 | 43 | newTestResults(state, action: PayloadAction) { 44 | console.log('payload: ',action.payload); 45 | state.testResults = action.payload; 46 | // console.log(state.testResults); 47 | for (let i = 0; i < state.testResults.length; i += 1) { 48 | state.expansionStatus.push(false); 49 | state.fixedStatus.push(false) 50 | } 51 | }, 52 | expandResult(state, action: PayloadAction) { 53 | state.expansionStatus[action.payload] = !state.expansionStatus[action.payload]; 54 | }, 55 | updateResult(state, action: PayloadAction) { 56 | // console.log('action.payload: ',action.payload) 57 | state.fixedStatus[action.payload] = true 58 | }, 59 | }, 60 | }); 61 | 62 | export const { newTestResults, expandResult, updateResult} = testResultSlice.actions; 63 | export default testResultSlice.reducer; -------------------------------------------------------------------------------- /src/renderer/components/IgnoreCards.tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect} from 'react'; 2 | import { useSelector, useDispatch } from 'react-redux'; 3 | import { ignoreItemsState, removeIgnoreArr } from '../Slices/ignoreItemsSlice'; 4 | import deleteIcon from '../../icons/deleteIcon.svg'; 5 | 6 | const IgnoreCards = (props:any) => { 7 | const state = useSelector(ignoreItemsState) 8 | const dispatch = useDispatch(); 9 | 10 | const handleDelete = (x:string)=>{ 11 | dispatch(removeIgnoreArr(x)) 12 | //@ts-expect-error 13 | bridgeAPI.removeIgnore(x); 14 | } 15 | 16 | useEffect(()=>{ 17 | //@ts-expect-error 18 | bridgeAPI.addIgnore(state.ignoreArr); 19 | }, [state.ignoreArr]) 20 | 21 | const renderArr = state.ignoreArr.map(x=>{ 22 | return( 23 |
24 |

25 | {x} 26 |

27 | {/* className="border border-transparent pl-4 pr-4 hover:bg-blueGray-500 hover:border-white" */} 28 | 31 |
32 | )}) 33 | 34 | return( 35 |
36 | {renderArr} 37 |
38 | ) 39 | } 40 | 41 | export default IgnoreCards -------------------------------------------------------------------------------- /src/renderer/components/Loader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Loader = () => { 4 | // let circleCommonClasses = 'h-2.5 w-2.5 bg-current rounded-full'; 5 | 6 | return ( 7 |
8 |
9 |
10 | ); 11 | }; 12 | 13 | export default Loader; -------------------------------------------------------------------------------- /src/renderer/components/NavBar.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react'; 2 | import { useSelector, useDispatch } from 'react-redux'; 3 | import openFolderIcon from '../../icons/openFolder.svg'; 4 | import icon from '../../icons/iconTransparent.svg'; 5 | import ResultDisplay from './ResultDisplay' 6 | import { RootState } from '../store'; 7 | import { newTestResults } from '../Slices/testResultSlice'; 8 | import IgnoreCards from './IgnoreCards' 9 | import { setReduxTextInput, removeReduxTextInput, addIgnoreArr, ignoreItemsState } from '../Slices/ignoreItemsSlice'; 10 | import { updateLoading } from '../Slices/loadingSlice'; 11 | 12 | 13 | const NavBar: () => JSX.Element = () => { 14 | const [textInput, setTextInput] = useState(''); 15 | 16 | const state = useSelector(ignoreItemsState) 17 | const name = useSelector((state: RootState) => state.testResults.projectName); 18 | const loading = useSelector((state: RootState) => state.loading); 19 | const dispatch = useDispatch(); 20 | 21 | const handleClickOpenFolder = () => { 22 | // dispatch(resetResults()) 23 | // console.log('laoding: ',loading) 24 | dispatch(updateLoading()) 25 | //@ts-expect-error 26 | bridgeAPI.openFolder(); 27 | 28 | //@ts-expect-error 29 | bridgeAPI.receiveData('preload:open-folder', (data: any)=>{ 30 | // dispatch(updateLoading()) 31 | // console.log('data: ', data); 32 | dispatch(newTestResults(data)); 33 | }); 34 | } 35 | 36 | const handleSubmit = (e: React.MouseEvent) =>{ 37 | e.preventDefault(); 38 | dispatch(setReduxTextInput(textInput)); 39 | dispatch(addIgnoreArr(textInput)); 40 | setTextInput('') 41 | dispatch(removeReduxTextInput()); 42 | } 43 | 44 | useEffect(()=>{ 45 | //console.log(state.ignoreArr); 46 | 47 | //@ts-expect-error 48 | bridgeAPI.addIgnore(state.ignoreArr); 49 | 50 | }, [state.ignoreArr]) 51 | 52 | return( 53 |
54 |
55 | {/* flex flex-col rounded overflow-auto h-auto border border-transparent border-shadow shadow-lg p-3 hover:bg-blueGray-500 hover:border-gray-darkest */} 56 |
59 |
60 | setTextInput(e.target.value)}> 61 | 62 |
63 | 64 |
65 |
66 | 67 |
68 |
69 | 70 |
71 | ); 72 | } 73 | 74 | export default NavBar; -------------------------------------------------------------------------------- /src/renderer/components/ResultDisplay.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | 3 | import { RootState } from '../store'; 4 | import { useSelector, useDispatch } from 'react-redux'; 5 | import { expandResult, updateResult } from '../Slices/testResultSlice'; 6 | import Loader from './Loader' 7 | import { updateLoading } from '../Slices/loadingSlice'; 8 | 9 | interface fileResult{ 10 | start: number 11 | status: string 12 | end: number 13 | testProp: string 14 | description: string 15 | failValue: boolean 16 | } 17 | 18 | const ResultDisplay = (): JSX.Element => { 19 | const newData = useSelector((state: RootState) => state.testResults.testResults); 20 | const expandBools = useSelector((state: RootState) => state.testResults.expansionStatus); 21 | const fixedBools = useSelector((state: RootState) => state.testResults.fixedStatus); 22 | const loading = useSelector((state: RootState) => state.loading.gettingData); 23 | const dispatch = useDispatch(); 24 | console.log('Expandbools: ', expandBools); 25 | 26 | const handleClickChangeValue = async (args:[string, string, string, boolean, number, string])=>{ 27 | //@ts-expect-error 28 | bridgeAPI.changeValue(args); 29 | 30 | //@ts-expect-error 31 | bridgeAPI.refreshCode(args); 32 | 33 | //@ts-expect-error 34 | bridgeAPI.receiveData('preload:refreshed-obj', (data: any)=>{ 35 | // if(data.status.includes('pass')) console.log(data); 36 | // data['id']=args[4] 37 | if(data.status.includes('pass')){ 38 | dispatch(updateResult(args[4])); 39 | dispatch(expandResult(args[4])) 40 | } 41 | // conditional[args[4]]=data 42 | }); 43 | } 44 | 45 | useEffect(()=>{ 46 | dispatch(updateLoading()) 47 | 48 | },[newData]) 49 | 50 | const conditional: Array = []; 51 | 52 | // let failCount: any = 0; 53 | // for(let i = 0; i < newData.length; i++){ 54 | // if(newData[i].fileResults["status"] == "fail" || newData[i].fileResults["status"] == "fail by default") failCount++; 55 | // } 56 | 57 | // if(failCount > 0) conditional.push( 58 | //
59 | //
60 | // Fails: 61 | // {failCount} 62 | //
63 | //
64 | // ); 65 | 66 | if(newData){ 67 | for (let i = 0; i < newData.length; i++) { 68 | const fileName:string = newData[i].fileName; 69 | const filePath:string = newData[i].filePath; 70 | const {start, status, end, testProp, failValue, description}:fileResult = newData[i].fileResults; 71 | 72 | conditional.push( 73 |
74 | 119 | {expandBools[i] &&
120 | Details: {description} 121 | {!testProp.includes('needToUpdateVersion') && status.includes('fail') && } 125 | 126 |
} 127 |
); 128 | }} 129 | 130 | return ( 131 |
132 |
133 | {loading &&} 134 | {!loading&&conditional} 135 |
136 |
137 | ); 138 | }; 139 | 140 | export default ResultDisplay; 141 | -------------------------------------------------------------------------------- /src/renderer/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import '../../src/styles.css'; 4 | import App from './App'; 5 | import { Provider } from 'react-redux'; 6 | import { store } from './store'; 7 | 8 | ReactDOM.render( 9 | 10 |
11 |
12 | 13 |
14 |
15 |
16 | , 17 | document.getElementById('root') 18 | ); -------------------------------------------------------------------------------- /src/renderer/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import { combineReducers, createStore } from 'redux'; 3 | //import { composeWithDevTools } from 'redux-devtools-extension'; 4 | import testResultsReducer from './Slices/testResultSlice' 5 | import ignoreItemsSlice from './Slices/ignoreItemsSlice'; 6 | import loadingReducer from './Slices/loadingSlice' 7 | 8 | export const store = configureStore({ 9 | reducer: { 10 | testResults: testResultsReducer, 11 | ignoreItems: ignoreItemsSlice, 12 | loading: loadingReducer, 13 | }, 14 | 15 | }); 16 | 17 | //const reducer = combineReducers({ testResults: testResultsReducer }); 18 | //export const store = createStore(reducer); 19 | 20 | export type RootState = ReturnType 21 | export type AppDispatch = typeof store.dispatch 22 | 23 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @tailwind components; 4 | 5 | @tailwind utilities; 6 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const colors = require('tailwindcss/colors') 2 | module.exports = { 3 | // mode: 'jit', 4 | purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'], 5 | darkMode: false, // or 'media' or 'class' 6 | theme: { 7 | extend: {}, 8 | colors: { 9 | transparent: 'transparent', 10 | current: 'currentColor', 11 | gray: { 12 | other: '#89898b', 13 | other2: '#1b1c1c', 14 | light: '#b0b0b1', 15 | DEFAULT: '#3a3b3d', 16 | medium: '#333333', 17 | dark: '#28292a', 18 | darkest: '#0f1010' 19 | }, 20 | blue: { 21 | light: '#e1edf3', 22 | DEFUALT: '#9dc6d8', 23 | dark: '#4e636c' 24 | }, 25 | peach: { 26 | light: '#fef3ef', 27 | DEFUALT: '#fac3af', 28 | dark: '#7d6157', 29 | other: '#af897a' 30 | }, 31 | teal: { 32 | light: '#E6FFFA', 33 | DEFUALT: '#38B2AC', 34 | dark: '#38B2AC', 35 | }, 36 | }, 37 | screens: { 38 | sm: '480px', 39 | md: '768px', 40 | lg: '976px', 41 | xl: '1440px', 42 | }, 43 | container: { 44 | center: true, 45 | padding: { 46 | DEFAULT: '1rem', 47 | sm: '2rem', 48 | lg: '4rem', 49 | xl: '5rem', 50 | '2xl': '6rem' 51 | }, 52 | }, 53 | fontFamily: { 54 | sans: ['Graphik', 'sans-serif'], 55 | serif: ['Merriweather', 'serif'], 56 | } 57 | }, 58 | variants: { 59 | extend: {}, 60 | }, 61 | plugins: [], 62 | } 63 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ 8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 9 | "lib": ["es2017", "dom"], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "./dist", /* Redirect output structure to the directory. */ 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true, /* Enable all strict type-checking options. */ 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ 44 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 45 | 46 | /* Module Resolution Options */ 47 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 48 | "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 49 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 50 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 51 | // "typeRoots": [], /* List of folders to include type definitions from. */ 52 | // "types": [], /* Type declaration files to be included in compilation. */ 53 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 54 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 55 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 56 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 57 | "resolveJsonModule": true, 58 | /* Source Map Options */ 59 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 60 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 61 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 62 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 63 | 64 | /* Experimental Options */ 65 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 66 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 67 | 68 | /* Advanced Options */ 69 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 70 | "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ 71 | "paths": { 72 | "@/*": [ 73 | "src/*" 74 | ] 75 | } 76 | }, 77 | } 78 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const electronConfigs = require('./webpack.electron.js'); 2 | const reactConfigs = require('./webpack.react.js'); 3 | 4 | module.exports = [ 5 | electronConfigs, 6 | reactConfigs, 7 | ]; -------------------------------------------------------------------------------- /webpack.electron.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | // Build Mode 5 | mode: "development", 6 | // Electron Entrypoint 7 | entry: './src/main.ts', 8 | target: 'electron-main', 9 | resolve: { 10 | alias: { 11 | ['@']: path.resolve(__dirname, 'src') 12 | }, 13 | extensions: ['.tsx', '.ts', '.js', 'jsx'], 14 | }, 15 | module: { 16 | rules: [{ 17 | // "test" is commonly used to match the file extension 18 | test: /\.(js(x?)|ts(x?))$/, 19 | // include all modules matching these conditions (/src folder) 20 | exclude: /node_modules/, 21 | // "exclude" should be used to exclude exceptions 22 | // try to prefer "include" when possible 23 | 24 | // the "loader" 25 | use: [{ loader: 'ts-loader' }] 26 | }] 27 | }, 28 | output: { 29 | path: path.resolve(__dirname, 'dist'), 30 | filename: 'main.js' 31 | }, 32 | } -------------------------------------------------------------------------------- /webpack.react.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | const path = require('path'); 4 | 5 | module.exports = { 6 | mode: "development", 7 | entry: './src/renderer/index.tsx', 8 | target: 'electron-renderer', 9 | devtool: 'source-map', 10 | devServer: { 11 | contentBase: path.join(__dirname, 'dist/renderer/index.js'), 12 | compress: true, 13 | hot: true, 14 | port: 8080, 15 | }, 16 | resolve: { 17 | alias: { 18 | ['@']: path.resolve(__dirname, 'src') 19 | }, 20 | extensions: ['.tsx', '.ts', '.js', '.jsx'], 21 | }, 22 | module: { 23 | rules: [ 24 | { 25 | test: /\.(js(x?)|ts(x?))$/, 26 | include: /src/, 27 | use: [{ loader: 'ts-loader' }] 28 | }, 29 | { 30 | test: /.(js|jsx)$/, 31 | include: /src/, 32 | loader: 'babel-loader', 33 | options: { 34 | presets: ['@babel/preset-env', '@babel/preset-react'], 35 | }, 36 | }, 37 | { 38 | test: /\.css$/, 39 | use: [ 40 | MiniCssExtractPlugin.loader, 41 | "css-loader", "postcss-loader", 42 | ], 43 | }, 44 | { 45 | test: /\.svg$/, 46 | use: [ 47 | { 48 | loader: 'svg-url-loader', 49 | options: { 50 | limit: 10000, 51 | }, 52 | }, 53 | ], 54 | }, 55 | ] 56 | }, 57 | output: { 58 | path: __dirname + '/dist/renderer', 59 | filename: 'index.js' 60 | }, 61 | devServer: { 62 | /*The bundled files will be available in the browser under this path. 63 | publicPath says that any request made to '/' will be served the development version of our bundle via localhost:8080. publicPath should match where we have index.html 64 | */ 65 | publicPath: '/dist', 66 | 67 | hot: true, 68 | // Tell the server where to serve content from. 69 | contentBase: path.resolve(__dirname, './dist'), 70 | watchContentBase: true, 71 | 72 | // Proxy says taht any request made to '/api' will be routed to our server on localhost:3000 73 | // proxy should match whatever is going to match your fetch request on your frontend. 74 | }, 75 | plugins: [ 76 | new HtmlWebpackPlugin({ 77 | template: './index.html', 78 | filename: path.join(__dirname, "./dist/index.html") 79 | }), 80 | new MiniCssExtractPlugin({ 81 | filename: "styles.css", 82 | chunkFilename: path.join(__dirname, "./dist/src/styles.css") 83 | }) 84 | ] 85 | }; --------------------------------------------------------------------------------