├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── README.md ├── assets └── .gitkeep ├── babel.config.js ├── electron ├── bridge.ts └── main.ts ├── jest.config.js ├── package.json ├── prettier.config.js ├── public └── index.html ├── renovate.json ├── src ├── @types │ ├── bridge.d.ts │ └── image.d.ts ├── App.tsx ├── components │ ├── Button │ │ ├── Button.spec.tsx │ │ ├── index.tsx │ │ └── styles.ts │ └── Greetings │ │ ├── Greetings.spec.tsx │ │ ├── index.tsx │ │ └── styles.ts ├── index.tsx └── styles │ └── GlobalStyle.ts ├── tests └── setupTests.ts ├── tsconfig.json ├── webpack ├── main.webpack.js ├── renderer.webpack.js └── rules.webpack.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | */.js 2 | node_modules 3 | dist 4 | webpack/*.js 5 | .webpack 6 | out -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true, 6 | "jest": true 7 | }, 8 | "extends": [ 9 | "standard", 10 | "prettier" 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "ecmaFeatures": { 15 | "jsx": true 16 | }, 17 | "ecmaVersion": 12, 18 | "sourceType": "module" 19 | }, 20 | "plugins": [ 21 | "react", 22 | "prettier", 23 | "@typescript-eslint" 24 | ], 25 | "rules": { 26 | "react/prop-types": "off" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | packages 4 | .webpack 5 | out 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Electron + TypeScript + React 2 | 3 | Boilerplate for a project using Electron, React and Typescript. 4 | 5 | ## Installation 6 | 7 | Use a package manager of your choice (npm, yarn, etc.) in order to install all dependencies 8 | 9 | ```bash 10 | yarn 11 | ``` 12 | 13 | ## Usage 14 | 15 | Just run `start` script. 16 | 17 | ```bash 18 | yarn start 19 | ``` 20 | 21 | ## Packaging 22 | 23 | To generate the project package based on the OS you're running on, just run: 24 | 25 | ```bash 26 | yarn package 27 | ``` 28 | 29 | ## Contributing 30 | 31 | Pull requests are always welcome 😃. 32 | 33 | ## License 34 | 35 | [MIT](https://choosealicense.com/licenses/mit/) 36 | -------------------------------------------------------------------------------- /assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diego3g/electron-typescript-react/9fbb498311d59906217c4c7c4db65a75943dc678/assets/.gitkeep -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@babel/preset-env', 4 | '@babel/preset-typescript', 5 | ['@babel/preset-react', { 6 | runtime: 'automatic' 7 | }] 8 | ], 9 | plugins: [ 10 | ['@babel/plugin-transform-runtime', { 11 | regenerator: true 12 | }] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /electron/bridge.ts: -------------------------------------------------------------------------------- 1 | import { contextBridge, ipcRenderer } from 'electron' 2 | 3 | export const api = { 4 | /** 5 | * Here you can expose functions to the renderer process 6 | * so they can interact with the main (electron) side 7 | * without security problems. 8 | * 9 | * The function below can accessed using `window.Main.sendMessage` 10 | */ 11 | 12 | sendMessage: (message: string) => { 13 | ipcRenderer.send('message', message) 14 | }, 15 | 16 | /** 17 | * Provide an easier way to listen to events 18 | */ 19 | on: (channel: string, callback: Function) => { 20 | ipcRenderer.on(channel, (_, data) => callback(data)) 21 | } 22 | } 23 | 24 | contextBridge.exposeInMainWorld('Main', api) 25 | -------------------------------------------------------------------------------- /electron/main.ts: -------------------------------------------------------------------------------- 1 | import { app, BrowserWindow, ipcMain } from 'electron' 2 | 3 | let mainWindow: BrowserWindow | null 4 | 5 | declare const MAIN_WINDOW_WEBPACK_ENTRY: string 6 | declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string 7 | 8 | // const assetsPath = 9 | // process.env.NODE_ENV === 'production' 10 | // ? process.resourcesPath 11 | // : app.getAppPath() 12 | 13 | function createWindow () { 14 | mainWindow = new BrowserWindow({ 15 | // icon: path.join(assetsPath, 'assets', 'icon.png'), 16 | width: 1100, 17 | height: 700, 18 | backgroundColor: '#191622', 19 | webPreferences: { 20 | nodeIntegration: false, 21 | contextIsolation: true, 22 | preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY 23 | } 24 | }) 25 | 26 | mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY) 27 | 28 | mainWindow.on('closed', () => { 29 | mainWindow = null 30 | }) 31 | } 32 | 33 | async function registerListeners () { 34 | /** 35 | * This comes from bridge integration, check bridge.ts 36 | */ 37 | ipcMain.on('message', (_, message) => { 38 | console.log(message) 39 | }) 40 | } 41 | 42 | app.on('ready', createWindow) 43 | .whenReady() 44 | .then(registerListeners) 45 | .catch(e => console.error(e)) 46 | 47 | app.on('window-all-closed', () => { 48 | if (process.platform !== 'darwin') { 49 | app.quit() 50 | } 51 | }) 52 | 53 | app.on('activate', () => { 54 | if (BrowserWindow.getAllWindows().length === 0) { 55 | createWindow() 56 | } 57 | }) 58 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | // All imported modules in your tests should be mocked automatically 6 | // automock: false, 7 | 8 | // Stop running tests after `n` failures 9 | // bail: 0, 10 | 11 | // The directory where Jest should store its cached dependency information 12 | // cacheDirectory: "/tmp/jest_rs", 13 | 14 | // Automatically clear mock calls and instances between every test 15 | clearMocks: true, 16 | 17 | // Indicates whether the coverage information should be collected while executing the test 18 | // collectCoverage: false, 19 | 20 | // An array of glob patterns indicating a set of files for which coverage information should be collected 21 | // collectCoverageFrom: undefined, 22 | 23 | // The directory where Jest should output its coverage files 24 | // coverageDirectory: undefined, 25 | 26 | // An array of regexp pattern strings used to skip coverage collection 27 | // coveragePathIgnorePatterns: [ 28 | // "/node_modules/" 29 | // ], 30 | 31 | // Indicates which provider should be used to instrument code for coverage 32 | // coverageProvider: "babel", 33 | 34 | // A list of reporter names that Jest uses when writing coverage reports 35 | // coverageReporters: [ 36 | // "json", 37 | // "text", 38 | // "lcov", 39 | // "clover" 40 | // ], 41 | 42 | // An object that configures minimum threshold enforcement for coverage results 43 | // coverageThreshold: undefined, 44 | 45 | // A path to a custom dependency extractor 46 | // dependencyExtractor: undefined, 47 | 48 | // Make calling deprecated APIs throw helpful error messages 49 | // errorOnDeprecated: false, 50 | 51 | // Force coverage collection from ignored files using an array of glob patterns 52 | // forceCoverageMatch: [], 53 | 54 | // A path to a module which exports an async function that is triggered once before all test suites 55 | // globalSetup: undefined, 56 | 57 | // A path to a module which exports an async function that is triggered once after all test suites 58 | // globalTeardown: undefined, 59 | 60 | // A set of global variables that need to be available in all test environments 61 | // globals: {}, 62 | 63 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 64 | // maxWorkers: "50%", 65 | 66 | // An array of directory names to be searched recursively up from the requiring module's location 67 | // moduleDirectories: [ 68 | // "node_modules" 69 | // ], 70 | 71 | // An array of file extensions your modules use 72 | // moduleFileExtensions: [ 73 | // "js", 74 | // "json", 75 | // "jsx", 76 | // "ts", 77 | // "tsx", 78 | // "node" 79 | // ], 80 | 81 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 82 | // moduleNameMapper: {}, 83 | 84 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 85 | // modulePathIgnorePatterns: [], 86 | 87 | // Activates notifications for test results 88 | // notify: false, 89 | 90 | // An enum that specifies notification mode. Requires { notify: true } 91 | // notifyMode: "failure-change", 92 | 93 | // A preset that is used as a base for Jest's configuration 94 | preset: 'ts-jest', 95 | 96 | // Run tests from one or more projects 97 | // projects: undefined, 98 | 99 | // Use this configuration option to add custom reporters to Jest 100 | // reporters: undefined, 101 | 102 | // Automatically reset mock state between every test 103 | // resetMocks: false, 104 | 105 | // Reset the module registry before running each individual test 106 | // resetModules: false, 107 | 108 | // A path to a custom resolver 109 | // resolver: undefined, 110 | 111 | // Automatically restore mock state between every test 112 | // restoreMocks: false, 113 | 114 | // The root directory that Jest should scan for tests and modules within 115 | // rootDir: undefined, 116 | 117 | // A list of paths to directories that Jest should use to search for files in 118 | // roots: [ 119 | // "" 120 | // ], 121 | 122 | // Allows you to use a custom runner instead of Jest's default test runner 123 | // runner: "jest-runner", 124 | 125 | // The paths to modules that run some code to configure or set up the testing environment before each test 126 | // setupFiles: [], 127 | 128 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 129 | setupFilesAfterEnv: ['./tests/setupTests.ts'], 130 | 131 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 132 | // snapshotSerializers: [], 133 | 134 | // The test environment that will be used for testing 135 | testEnvironment: 'jsdom' 136 | 137 | // Options that will be passed to the testEnvironment 138 | // testEnvironmentOptions: {}, 139 | 140 | // Adds a location field to test results 141 | // testLocationInResults: false, 142 | 143 | // The glob patterns Jest uses to detect test files 144 | // testMatch: [ 145 | // "**/__tests__/**/*.[jt]s?(x)", 146 | // "**/?(*.)+(spec|test).[tj]s?(x)" 147 | // ], 148 | 149 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 150 | // testPathIgnorePatterns: [ 151 | // "/node_modules/" 152 | // ], 153 | 154 | // The regexp pattern or array of patterns that Jest uses to detect test files 155 | // testRegex: [], 156 | 157 | // This option allows the use of a custom results processor 158 | // testResultsProcessor: undefined, 159 | 160 | // This option allows use of a custom test runner 161 | // testRunner: "jasmine2", 162 | 163 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 164 | // testURL: "http://localhost", 165 | 166 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 167 | // timers: "real", 168 | 169 | // A map from regular expressions to paths to transformers 170 | // transform: undefined, 171 | 172 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 173 | // transformIgnorePatterns: [ 174 | // "/node_modules/" 175 | // ], 176 | 177 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 178 | // unmockedModulePathPatterns: undefined, 179 | 180 | // Indicates whether each individual test should be reported during the run 181 | // verbose: undefined, 182 | 183 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 184 | // watchPathIgnorePatterns: [], 185 | 186 | // Whether to use watchman for file crawling 187 | // watchman: true, 188 | } 189 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-react-typescript", 3 | "author": "Diego Fernandes ", 4 | "version": "2.0.0", 5 | "description": "An Electron boilerplate including TypeScript, React, Jest and ESLint.", 6 | "main": "./.webpack/main/index.js", 7 | "scripts": { 8 | "start": "electron-forge start", 9 | "package": "electron-forge package", 10 | "make": "electron-forge make", 11 | "release": "electron-forge publish", 12 | "lint": "eslint . --ext js,ts", 13 | "test": "jest" 14 | }, 15 | "keywords": [], 16 | "license": "MIT", 17 | "dependencies": { 18 | "react": "17.0.2", 19 | "react-dom": "17.0.2", 20 | "react-hot-loader": "4.13.0", 21 | "styled-components": "5.3.0" 22 | }, 23 | "devDependencies": { 24 | "@babel/core": "7.14.6", 25 | "@babel/plugin-transform-runtime": "7.14.5", 26 | "@babel/preset-env": "7.14.5", 27 | "@babel/preset-react": "7.14.5", 28 | "@babel/preset-typescript": "7.14.5", 29 | "@electron-forge/cli": "6.0.0-beta.57", 30 | "@electron-forge/maker-deb": "6.0.0-beta.57", 31 | "@electron-forge/maker-rpm": "6.0.0-beta.57", 32 | "@electron-forge/maker-squirrel": "6.0.0-beta.57", 33 | "@electron-forge/maker-zip": "6.0.0-beta.57", 34 | "@electron-forge/plugin-webpack": "6.0.0-beta.57", 35 | "@marshallofsound/webpack-asset-relocator-loader": "0.5.0", 36 | "@testing-library/jest-dom": "5.14.1", 37 | "@testing-library/react": "11.2.7", 38 | "@types/electron-devtools-installer": "2.2.0", 39 | "@types/jest": "26.0.23", 40 | "@types/react": "17.0.11", 41 | "@types/react-dom": "17.0.8", 42 | "@types/styled-components": "5.1.10", 43 | "@typescript-eslint/eslint-plugin": "4.27.0", 44 | "@typescript-eslint/parser": "4.27.0", 45 | "babel-loader": "8.2.2", 46 | "cross-env": "7.0.3", 47 | "electron": "13.1.2", 48 | "eslint": "7.29.0", 49 | "eslint-config-prettier": "8.3.0", 50 | "eslint-config-standard": "16.0.3", 51 | "eslint-plugin-import": "2.23.4", 52 | "eslint-plugin-node": "11.1.0", 53 | "eslint-plugin-prettier": "3.4.0", 54 | "eslint-plugin-promise": "5.1.0", 55 | "eslint-plugin-react": "7.24.0", 56 | "eslint-plugin-standard": "5.0.0", 57 | "file-loader": "^6.2.0", 58 | "jest": "27.0.4", 59 | "npm-run-all": "4.1.5", 60 | "prettier": "2.3.1", 61 | "ts-jest": "27.0.3", 62 | "typescript": "4.3.4", 63 | "wait-on": "5.3.0" 64 | }, 65 | "config": { 66 | "forge": { 67 | "packagerConfig": { 68 | "name": "Electron starter", 69 | "executableName": "electron-starter", 70 | "icon": "assets/icon", 71 | "extraResource": [ 72 | "assets" 73 | ] 74 | }, 75 | "plugins": [ 76 | [ 77 | "@electron-forge/plugin-webpack", 78 | { 79 | "mainConfig": "./webpack/main.webpack.js", 80 | "renderer": { 81 | "config": "./webpack/renderer.webpack.js", 82 | "entryPoints": [ 83 | { 84 | "html": "./public/index.html", 85 | "js": "./src/index.tsx", 86 | "name": "main_window", 87 | "preload": { 88 | "js": "./electron/bridge.ts" 89 | } 90 | } 91 | ] 92 | } 93 | } 94 | ] 95 | ], 96 | "makers": [ 97 | { 98 | "name": "@electron-forge/maker-squirrel", 99 | "config": { 100 | "name": "Electron Starter" 101 | } 102 | }, 103 | { 104 | "name": "@electron-forge/maker-zip", 105 | "platforms": [ 106 | "darwin" 107 | ] 108 | }, 109 | { 110 | "name": "@electron-forge/maker-deb", 111 | "config": {} 112 | }, 113 | { 114 | "name": "@electron-forge/maker-rpm", 115 | "config": {} 116 | } 117 | ] 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | singleQuote: true, 4 | arrowParens: 'avoid', 5 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Electron starter 9 | 10 | 13 | 14 | 15 |
16 | 17 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "semanticCommits": true, 4 | "stabilityDays": 3, 5 | "prCreation": "not-pending", 6 | "labels": ["dependencies"] 7 | } -------------------------------------------------------------------------------- /src/@types/bridge.d.ts: -------------------------------------------------------------------------------- 1 | import { api } from '../../electron/bridge' 2 | 3 | declare global { 4 | // eslint-disable-next-line 5 | interface Window { 6 | Main: typeof api 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/@types/image.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png'; 2 | declare module '*.jpeg'; 3 | declare module '*.jpg'; 4 | declare module '*.gif'; -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { GlobalStyle } from './styles/GlobalStyle' 2 | 3 | import { Greetings } from './components/Greetings' 4 | 5 | export function App() { 6 | return ( 7 | <> 8 | 9 | 10 | 11 | ) 12 | } -------------------------------------------------------------------------------- /src/components/Button/Button.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react' 2 | import { Button } from './index' 3 | 4 | test('button should renders', () => { 5 | const { getByText } = render() 6 | 7 | expect(getByText('ButtonContent')).toBeTruthy() 8 | expect(getByText('ButtonContent')).toHaveAttribute('type', 'button') 9 | }) 10 | -------------------------------------------------------------------------------- /src/components/Button/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode, ButtonHTMLAttributes } from 'react' 2 | 3 | import { Container } from './styles' 4 | 5 | type ButtonProps = { 6 | children: ReactNode; 7 | } & ButtonHTMLAttributes; 8 | 9 | export function Button (props: ButtonProps) { 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /src/components/Button/styles.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const Container = styled.button` 4 | height: 42px; 5 | padding: 0 24px; 6 | 7 | display: flex; 8 | align-items: center; 9 | justify-content: center; 10 | 11 | background: #8257e6; 12 | border-radius: 8px; 13 | border: 0; 14 | 15 | color: #FFF; 16 | font-size: 16px; 17 | font-weight: bold; 18 | 19 | cursor: pointer; 20 | 21 | &:hover { 22 | filter: brightness(0.9); 23 | } 24 | 25 | &:active { 26 | filter: brightness(0.7); 27 | } 28 | ` 29 | -------------------------------------------------------------------------------- /src/components/Greetings/Greetings.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react' 2 | 3 | import { Greetings } from './index' 4 | 5 | test('Greetings should renders', () => { 6 | const { getByText, getByAltText } = render() 7 | 8 | expect( 9 | getByText('An Electron boilerplate including TypeScript, React, Jest and ESLint.') 10 | ).toBeTruthy() 11 | expect(getByAltText('ReactJS logo')).toBeTruthy() 12 | }) 13 | -------------------------------------------------------------------------------- /src/components/Greetings/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '../Button' 2 | import { Container, Image, Text } from './styles' 3 | 4 | export function Greetings() { 5 | function handleSayHello() { 6 | window.Main.sendMessage('Hello World'); 7 | 8 | console.log('Message sent! Check main process log in terminal.') 9 | } 10 | 11 | return ( 12 | 13 | ReactJS logo 17 | An Electron boilerplate including TypeScript, React, Jest and ESLint. 18 | 19 | 20 | ) 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/components/Greetings/styles.ts: -------------------------------------------------------------------------------- 1 | import styled, { keyframes } from 'styled-components' 2 | 3 | const rotate = keyframes` 4 | from { 5 | transform: rotate(0deg); 6 | } 7 | to { 8 | transform: rotate(360deg); 9 | } 10 | ` 11 | 12 | export const Container = styled.div` 13 | height: 100vh; 14 | padding: 25px; 15 | display: flex; 16 | flex-direction: column; 17 | align-items: center; 18 | justify-content: center; 19 | 20 | button { 21 | margin-top: 24px; 22 | } 23 | ` 24 | 25 | export const Image = styled.img` 26 | width: 240px; 27 | animation: ${rotate} 15s linear infinite; 28 | ` 29 | 30 | export const Text = styled.p` 31 | margin-top: 24px; 32 | font-size: 18px; 33 | ` 34 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from "react-dom"; 2 | import { App } from "./App"; 3 | 4 | ReactDOM.render(, document.getElementById("root")); 5 | -------------------------------------------------------------------------------- /src/styles/GlobalStyle.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components' 2 | 3 | export const GlobalStyle = createGlobalStyle` 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | box-sizing: border-box; 8 | } 9 | 10 | body { 11 | font-family: Arial, Helvetica, sans-serif; 12 | font-size: 16px; 13 | color: #E1E1E6; 14 | } 15 | ` 16 | -------------------------------------------------------------------------------- /tests/setupTests.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom/extend-expect' 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "lib": [ 6 | "dom", 7 | "es2015", 8 | "es2016", 9 | "es2017" 10 | ], 11 | "allowJs": true, 12 | "jsx": "react-jsx", 13 | "sourceMap": true, 14 | "outDir": "./dist", 15 | "strict": true , 16 | "esModuleInterop": true, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /webpack/main.webpack.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | resolve: { 3 | extensions: ['.ts', '.js'] 4 | }, 5 | entry: './electron/main.ts', 6 | module: { 7 | rules: require('./rules.webpack'), 8 | } 9 | } -------------------------------------------------------------------------------- /webpack/renderer.webpack.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | resolve: { 3 | extensions: ['.ts', '.tsx', '.js'] 4 | }, 5 | module: { 6 | rules: require('./rules.webpack'), 7 | }, 8 | } -------------------------------------------------------------------------------- /webpack/rules.webpack.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | test: /\.node$/, 4 | use: 'node-loader', 5 | }, 6 | { 7 | test: /\.(m?js|node)$/, 8 | parser: { amd: false }, 9 | use: { 10 | loader: '@marshallofsound/webpack-asset-relocator-loader', 11 | options: { 12 | outputAssetBase: 'native_modules', 13 | }, 14 | }, 15 | }, 16 | { 17 | test: /\.(js|ts|tsx)$/, 18 | exclude: /node_modules/, 19 | use: { 20 | loader: 'babel-loader' 21 | } 22 | }, 23 | { 24 | test: /\.(png|jpe?g|gif)$/i, 25 | loader: 'file-loader', 26 | options: { 27 | name: '[path][name].[ext]', 28 | }, 29 | } 30 | ] --------------------------------------------------------------------------------