├── .config ├── webpack.main.config.js ├── webpack.plugins.js ├── webpack.renderer.config.js └── webpack.rules.js ├── .gitignore ├── README.md ├── package.json ├── src ├── main │ └── main.ts └── renderer │ ├── App.tsx │ ├── index.html │ ├── renderer.tsx │ ├── screens │ └── Home.tsx │ ├── store │ └── store.ts │ └── theme.ts ├── tsconfig.json └── yarn.lock /.config/webpack.main.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const FilterWarningsPlugin = require('webpack-filter-warnings-plugin') 3 | const plugins = require('./webpack.plugins') 4 | 5 | function srcPaths(src) { 6 | return path.join(__dirname, src) 7 | } 8 | 9 | module.exports = { 10 | /** 11 | * This is the main entry point for your application, it's the first file 12 | * that runs in the main process. 13 | */ 14 | entry: './src/main/main.ts', 15 | resolve: { 16 | alias: { 17 | '@': srcPaths('src'), 18 | '@main': srcPaths('src/main'), 19 | '@renderer': srcPaths('src/renderer'), 20 | }, 21 | extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'], 22 | }, 23 | // Put your normal webpack config below here 24 | module: { 25 | rules: require('./webpack.rules'), 26 | }, 27 | plugins: [ 28 | // https://typeorm.io/#/faq/how-to-use-webpack-for-the-backend 29 | //ignore the drivers you don't want. This is the complete list of all drivers -- remove the suppressions for drivers you want to use. 30 | new FilterWarningsPlugin({ 31 | // prettier-ignore 32 | exclude: [/mongodb/, /mssql/, /mysql/, /mysql2/, /oracledb/, /pg/, /pg-native/, /pg-query-stream/, /redis/, /sqlite3/] 33 | }), 34 | ...plugins, 35 | ], 36 | 37 | // for https://typeorm.io/#/faq/bundling-migration-files 38 | optimization: { 39 | minimize: false, 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /.config/webpack.plugins.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin') 3 | 4 | module.exports = [ 5 | new webpack.DefinePlugin({ 6 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 7 | }), 8 | new ForkTsCheckerWebpackPlugin({ 9 | async: false, 10 | }), 11 | ] 12 | -------------------------------------------------------------------------------- /.config/webpack.renderer.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const plugins = require('./webpack.plugins') 3 | const rules = require('./webpack.rules') 4 | 5 | function srcPaths(src) { 6 | return path.join(__dirname, src) 7 | } 8 | 9 | rules.push({ 10 | test: /\.css$/, 11 | use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], 12 | }) 13 | 14 | module.exports = { 15 | resolve: { 16 | alias: { 17 | '@': srcPaths('src'), 18 | '@main': srcPaths('src/main'), 19 | '@renderer': srcPaths('src/renderer'), 20 | }, 21 | extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'], 22 | }, 23 | // Put your normal webpack config below here 24 | module: { 25 | rules, 26 | }, 27 | plugins, 28 | } 29 | -------------------------------------------------------------------------------- /.config/webpack.rules.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | // Add support for native node modules 3 | { 4 | test: /\.node$/, 5 | use: 'node-loader', 6 | }, 7 | { 8 | test: /\.(m?js|node)$/, 9 | parser: { amd: false }, 10 | use: { 11 | loader: '@marshallofsound/webpack-asset-relocator-loader', 12 | options: { 13 | outputAssetBase: 'native_modules', 14 | }, 15 | }, 16 | }, 17 | // Put your webpack loader rules in this array. This is where you would put 18 | // your ts-loader configuration for instance: 19 | { 20 | test: /\.tsx?$/, 21 | exclude: /(node_modules|.webpack)/, 22 | loaders: [ 23 | { 24 | loader: 'ts-loader', 25 | options: { 26 | transpileOnly: true, 27 | }, 28 | }, 29 | ], 30 | }, 31 | ] 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | .DS_Store 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # TypeScript cache 43 | *.tsbuildinfo 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | .env.test 63 | 64 | # parcel-bundler cache (https://parceljs.org/) 65 | .cache 66 | 67 | # next.js build output 68 | .next 69 | 70 | # nuxt.js build output 71 | .nuxt 72 | 73 | # vuepress build output 74 | .vuepress/dist 75 | 76 | # Serverless directories 77 | .serverless/ 78 | 79 | # FuseBox cache 80 | .fusebox/ 81 | 82 | # DynamoDB Local files 83 | .dynamodb/ 84 | 85 | # Webpack 86 | .webpack/ 87 | 88 | # Electron-Forge 89 | out/ 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Electron-Forge + React + TypeScript Boilerplate 🚀 2 | 3 | Based on https://ankitbko.github.io/2019/08/electron-forge-with-react-and-typescript/ 4 | So thanks [@ankitbko](https://twitter.com/ankitbko) for this great and easy blog post. 🙏 5 | 6 | ### Features 7 | 8 | - [@emotion/core](https://www.npmjs.com/package/@emotion/core) 9 | - [@mdx-js/react](https://www.npmjs.com/package/@mdx-js/react) 10 | - [@reach/router](https://www.npmjs.com/package/@reach/router) 11 | - [cheerio](https://www.npmjs.com/package/cheerio) 12 | - [computer-name](https://www.npmjs.com/package/computer-name) 13 | - [cuid](https://www.npmjs.com/package/cuid) 14 | - [date-fns](https://www.npmjs.com/package/date-fns) 15 | - [electron-devtools-installer](https://www.npmjs.com/package/electron-devtools-installer) 16 | - [electron-is-dev](https://www.npmjs.com/package/electron-is-dev) 17 | - [electron-squirrel-startup](https://www.npmjs.com/package/electron-squirrel-startup) 18 | - [electron-store](https://www.npmjs.com/package/electron-store) 19 | - [filenamify](https://www.npmjs.com/package/filenamify) 20 | - [lodash](https://www.npmjs.com/package/lodash) 21 | - [node-machine-id](https://www.npmjs.com/package/node-machine-id) 22 | - [portfinder](https://www.npmjs.com/package/portfinder) 23 | - [query-string](https://www.npmjs.com/package/query-string) 24 | - [ramda](https://www.npmjs.com/package/ramda) 25 | - [react-dom](https://www.npmjs.com/package/react-dom) 26 | - [react-sweet-state](https://www.npmjs.com/package/react-sweet-state) 27 | - [react](https://www.npmjs.com/package/react) 28 | - [theme-ui](https://www.npmjs.com/package/theme-ui) waiting for [#423](https://github.com/system-ui/theme-ui/issues/423) 29 | - [typeorm](https://www.npmjs.com/package/typeorm) 30 | - [sqlite3](https://www.npmjs.com/package/sqlite3) 31 | 32 | ### Inspiration 33 | 34 | - https://github.com/sindresorhus/tsconfig/blob/master/tsconfig.json 35 | - https://github.com/LekoArts/gatsby-themes/blob/master/tsconfig.json 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-forge-react-typescript-boilerplate", 3 | "productname": "electron-forge-react-typescript-boilerplate", 4 | "version": "1.0.0", 5 | "description": "My Electron application description", 6 | "main": ".webpack/main", 7 | "scripts": { 8 | "start": "electron-forge start", 9 | "package": "electron-forge package", 10 | "make": "electron-forge make", 11 | "publish": "electron-forge publish", 12 | "lint": "echo \"No linting configured\"" 13 | }, 14 | "keywords": [], 15 | "author": { 16 | "name": "CanRau", 17 | "email": "cansrau@gmail.com", 18 | "url": "https://www.canrau.com" 19 | }, 20 | "license": "MIT", 21 | "config": { 22 | "forge": { 23 | "packagerConfig": {}, 24 | "makers": [ 25 | { 26 | "name": "@electron-forge/maker-squirrel", 27 | "config": { 28 | "name": "desktop_electron_forge" 29 | } 30 | }, 31 | { 32 | "name": "@electron-forge/maker-zip", 33 | "platforms": [ 34 | "darwin" 35 | ] 36 | }, 37 | { 38 | "name": "@electron-forge/maker-deb", 39 | "config": {} 40 | }, 41 | { 42 | "name": "@electron-forge/maker-rpm", 43 | "config": {} 44 | } 45 | ], 46 | "plugins": [ 47 | [ 48 | "@electron-forge/plugin-webpack", 49 | { 50 | "mainConfig": ".config/webpack.main.config.js", 51 | "renderer": { 52 | "config": ".config/webpack.renderer.config.js", 53 | "entryPoints": [ 54 | { 55 | "html": "./src/renderer/index.html", 56 | "js": "./src/renderer/renderer.tsx", 57 | "name": "main_window" 58 | } 59 | ] 60 | } 61 | } 62 | ] 63 | ] 64 | } 65 | }, 66 | "dependencies": { 67 | "@emotion/core": "^10.0.21", 68 | "@mdx-js/react": "^1.5.1", 69 | "@reach/router": "^1.2.1", 70 | "@types/cuid": "^1.3.0", 71 | "@types/lodash": "^4.14.144", 72 | "@types/node": "^12.11.7", 73 | "@types/ramda": "^0.26.33", 74 | "@types/reach__router": "^1.2.6", 75 | "@types/react": "^16.9.10", 76 | "@types/react-dom": "^16.9.2", 77 | "@types/theme-ui": "^0.2.3", 78 | "cheerio": "^1.0.0-rc.3", 79 | "computer-name": "^0.1.0", 80 | "cuid": "^2.1.6", 81 | "date-fns": "^2.5.0", 82 | "electron-devtools-installer": "^2.2.4", 83 | "electron-is-dev": "^1.1.0", 84 | "electron-squirrel-startup": "^1.0.0", 85 | "electron-store": "^5.0.0", 86 | "filenamify": "^4.1.0", 87 | "lodash": "^4.17.19", 88 | "node-machine-id": "^1.1.12", 89 | "portfinder": "^1.0.25", 90 | "query-string": "^6.8.3", 91 | "ramda": "^0.26.1", 92 | "react": "^16.11.0", 93 | "react-dom": "^16.11.0", 94 | "react-sweet-state": "^1.1.1", 95 | "reflect-metadata": "^0.1.13", 96 | "sqlite3": "^4.1.0", 97 | "theme-ui": "^0.2.44", 98 | "typeorm": "^0.2.20" 99 | }, 100 | "devDependencies": { 101 | "@electron-forge/cli": "6.0.0-beta.45", 102 | "@electron-forge/maker-deb": "6.0.0-beta.45", 103 | "@electron-forge/maker-rpm": "6.0.0-beta.45", 104 | "@electron-forge/maker-squirrel": "6.0.0-beta.45", 105 | "@electron-forge/maker-zip": "6.0.0-beta.45", 106 | "@electron-forge/plugin-webpack": "6.0.0-beta.45", 107 | "@marshallofsound/webpack-asset-relocator-loader": "^0.5.0", 108 | "css-loader": "^3.0.0", 109 | "electron": "7.2.4", 110 | "node-loader": "^0.6.0", 111 | "style-loader": "^0.23.1", 112 | "webpack-filter-warnings-plugin": "^1.2.1" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/main.ts: -------------------------------------------------------------------------------- 1 | import { app, BrowserWindow } from 'electron' 2 | declare var MAIN_WINDOW_WEBPACK_ENTRY: any 3 | 4 | // Handle creating/removing shortcuts on Windows when installing/uninstalling. 5 | if (require('electron-squirrel-startup')) { 6 | // eslint-disable-line global-require 7 | app.quit() 8 | } 9 | 10 | // Keep a global reference of the window object, if you don't, the window will 11 | // be closed automatically when the JavaScript object is garbage collected. 12 | let mainWindow: any 13 | 14 | const createWindow = () => { 15 | // Create the browser window. 16 | mainWindow = new BrowserWindow({ 17 | width: 800, 18 | height: 600, 19 | }) 20 | 21 | // and load the index.html of the app. 22 | mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY) 23 | 24 | // Open the DevTools. 25 | mainWindow.webContents.openDevTools() 26 | 27 | // Emitted when the window is closed. 28 | mainWindow.on('closed', () => { 29 | // Dereference the window object, usually you would store windows 30 | // in an array if your app supports multi windows, this is the time 31 | // when you should delete the corresponding element. 32 | mainWindow = null 33 | }) 34 | } 35 | 36 | // This method will be called when Electron has finished 37 | // initialization and is ready to create browser windows. 38 | // Some APIs can only be used after this event occurs. 39 | app.on('ready', createWindow) 40 | 41 | // Quit when all windows are closed. 42 | app.on('window-all-closed', () => { 43 | // On OS X it is common for applications and their menu bar 44 | // to stay active until the user quits explicitly with Cmd + Q 45 | if (process.platform !== 'darwin') { 46 | app.quit() 47 | } 48 | }) 49 | 50 | app.on('activate', () => { 51 | // On OS X it's common to re-create a window in the app when the 52 | // dock icon is clicked and there are no other windows open. 53 | if (mainWindow === null) { 54 | createWindow() 55 | } 56 | }) 57 | -------------------------------------------------------------------------------- /src/renderer/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { ThemeProvider } from 'theme-ui' 3 | import { 4 | Router, 5 | Link, 6 | createMemorySource, 7 | createHistory, 8 | LocationProvider, 9 | RouteComponentProps, 10 | } from '@reach/router' 11 | import Home from './screens/Home' 12 | import theme from './theme' 13 | 14 | const source = createMemorySource('/') 15 | const history = createHistory(source) 16 | 17 | export const App = () => { 18 | return ( 19 | 20 | 21 | 22 | } /> 23 | 24 | 25 | 26 | ) 27 | } 28 | 29 | // Based on https://github.com/reach/router/issues/141#issuecomment-451646939 30 | const RouterPage = ( 31 | props: { pageComponent: JSX.Element } & RouteComponentProps 32 | ) => props.pageComponent 33 | -------------------------------------------------------------------------------- /src/renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello World! 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /src/renderer/renderer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { render } from 'react-dom' 3 | import { App } from './App' 4 | 5 | render(, document.getElementById('root')) 6 | -------------------------------------------------------------------------------- /src/renderer/screens/Home.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { useStore } from '../store/store' 3 | 4 | export default () => { 5 | const [state, actions] = useStore() 6 | return

Hello superpowers 🚀

7 | } 8 | -------------------------------------------------------------------------------- /src/renderer/store/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, createHook } from 'react-sweet-state' 2 | 3 | import { createContext } from 'react' 4 | 5 | export const StoreContext = createContext({}) 6 | StoreContext.displayName = 'StoreContext' 7 | 8 | const initialState = {} 9 | 10 | const actions = {} 11 | 12 | const Store = createStore({ initialState, actions }) 13 | 14 | export const useStore = createHook(Store) 15 | -------------------------------------------------------------------------------- /src/renderer/theme.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | fonts: { 3 | body: 'system-ui, sans-serif', 4 | }, 5 | colors: { 6 | text: '#ccc', 7 | background: '#222', 8 | primary: '#33e', 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "lib": ["dom", "dom.iterable", "esnext", "es2015", "es2017"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "module": "commonjs", 14 | "moduleResolution": "node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "noEmit": true, 18 | "jsx": "react", 19 | "baseUrl": ".", 20 | "paths": { 21 | "@/*": ["src/*"], 22 | "@main/*": ["src/main/*"], 23 | "@renderer/*": ["src/renderer/*"], 24 | } 25 | }, 26 | "include": ["src"], 27 | "exclude": ["node_modules"] 28 | } --------------------------------------------------------------------------------