├── @test
├── config
│ ├── fileMock.js
│ ├── setupTests.js
│ └── componentsMock.js
└── src
│ ├── routes
│ └── index.test.js
│ ├── screens
│ └── Home
│ │ └── Home.test.js
│ ├── components
│ ├── Home
│ │ ├── Path.test.js
│ │ └── HelloWorld.test.js
│ └── @shared
│ │ ├── Footer.test.js
│ │ └── Header.test.js
│ └── layouts
│ └── App.test.js
├── src
├── screens
│ └── Home
│ │ ├── Home.scss
│ │ └── Home.js
├── components
│ ├── Home
│ │ ├── Path.scss
│ │ ├── HelloWorld.scss
│ │ ├── HelloWorld.js
│ │ └── Path.js
│ └── @shared
│ │ ├── Footer.scss
│ │ ├── Header.scss
│ │ ├── Footer.js
│ │ └── Header.js
├── layouts
│ ├── App.scss
│ └── App.js
├── assets
│ └── images
│ │ └── logo.png
├── themes
│ ├── App.global.scss
│ └── Ant.vars.scss
├── config
│ └── index.js
├── routes
│ └── Root.js
└── index.js
├── .npmignore
├── public
├── favicon.png
└── index.html
├── .editorconfig
├── postcss.config.js
├── .eslintrc
├── .babelrc
├── .gitignore
├── LICENSE
├── README.md
├── main.js
├── package.json
└── webpack.config.babel.js
/@test/config/fileMock.js:
--------------------------------------------------------------------------------
1 | export default 'file'
2 |
--------------------------------------------------------------------------------
/src/screens/Home/Home.scss:
--------------------------------------------------------------------------------
1 | .home {
2 | padding: 0 50px;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/Home/Path.scss:
--------------------------------------------------------------------------------
1 | .path {
2 | margin: 16px 0;
3 | }
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | _ignore/
2 | docs/
3 | release/
4 | dist/
5 | .editorconfig
6 | __snapshots__
7 |
8 |
--------------------------------------------------------------------------------
/src/layouts/App.scss:
--------------------------------------------------------------------------------
1 | .app {
2 | padding: 10px 15px 15px 15px;
3 | background: #f0f2f5;
4 | }
5 |
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bytesleo/electron-react-ant-boilerplate/HEAD/public/favicon.png
--------------------------------------------------------------------------------
/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bytesleo/electron-react-ant-boilerplate/HEAD/src/assets/images/logo.png
--------------------------------------------------------------------------------
/src/components/Home/HelloWorld.scss:
--------------------------------------------------------------------------------
1 | .hello-world {
2 | background: #fff;
3 | padding: 24px;
4 | height: calc(100vh - 190px);
5 | }
--------------------------------------------------------------------------------
/@test/config/setupTests.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import { configure } from "enzyme";
3 | import Adapter from "enzyme-adapter-react-16";
4 | // Adapter
5 | configure({ adapter: new Adapter() });
6 |
--------------------------------------------------------------------------------
/src/components/@shared/Footer.scss:
--------------------------------------------------------------------------------
1 | .footer {
2 | text-align: center;
3 | padding: 5px 10px 20px 10px;
4 |
5 | .logo {
6 | width: 20px;
7 | height: 20px;
8 | margin-right: 5px;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/themes/App.global.scss:
--------------------------------------------------------------------------------
1 | // Variables
2 | @import "./Ant.vars.scss";
3 |
4 | // Example import .css
5 | //@import '~name_in_node_modules/dist/style.css';
6 |
7 | // Global styles
8 | body {
9 | background-color: #f0f2f5;
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/@shared/Header.scss:
--------------------------------------------------------------------------------
1 | .header {
2 | .logo {
3 | width: 120px;
4 | height: 31px;
5 | background: rgba(255, 255, 255, 0.2);
6 | margin: 16px 24px 16px 0;
7 | float: left;
8 | }
9 |
10 | .menu {
11 | line-height: 64px;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = crlf
8 | indent_size = 2
9 | indent_style = space
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/src/config/index.js:
--------------------------------------------------------------------------------
1 | // Example url API
2 | const URL_API = "http://localhost:8000";
3 |
4 | // Example Key JWT
5 | const KEY_JWT = "ABC123456";
6 |
7 | // Example Images
8 | const IMAGES = {
9 | LOGO: require("@/assets/images/logo.png")
10 | };
11 |
12 | export { URL_API, KEY_JWT, IMAGES };
13 |
--------------------------------------------------------------------------------
/src/routes/Root.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React from "react";
3 | import { HashRouter, Switch, Route } from "react-router-dom";
4 | // Screens
5 | import Home from "@/screens/Home/Home";
6 |
7 | const Routes = () => (
8 |
9 |
10 |
11 |
12 |
13 | );
14 |
15 | export default Routes;
16 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | My App
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/@test/src/routes/index.test.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React from "react";
3 | import { shallow } from "enzyme";
4 | import toJson from "enzyme-to-json";
5 |
6 | // Module
7 | import Root from "@/routes/Root";
8 |
9 | describe("Routes", () => {
10 | it('should render correctly in "debug" mode', () => {
11 | const component = shallow();
12 | expect(toJson(component)).toMatchSnapshot();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/@test/src/screens/Home/Home.test.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React from "react";
3 | import { shallow } from "enzyme";
4 | import toJson from "enzyme-to-json";
5 | // Module
6 | import Home from "@/screens/Home/Home.js";
7 |
8 | describe("Screen Home", () => {
9 | it('should render correctly in "debug" mode', () => {
10 | const component = shallow();
11 | expect(toJson(component)).toMatchSnapshot();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/@test/src/components/Home/Path.test.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React from "react";
3 | import { shallow } from "enzyme";
4 | import toJson from "enzyme-to-json";
5 | // Module
6 | import Path from "@/components/Home/Path";
7 |
8 | describe("Component Path", () => {
9 | it('should render correctly in "debug" mode', () => {
10 | const component = shallow();
11 | expect(toJson(component)).toMatchSnapshot();
12 |
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/@test/config/componentsMock.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React from "react";
3 | import PropTypes from "prop-types";
4 |
5 | module.exports = new Proxy(
6 | {},
7 | {
8 | get: (target, property) => {
9 | const Mock = ({ children }) => {children};
10 |
11 | Mock.displayName = property;
12 | Mock.propTypes = {
13 | children: PropTypes.any
14 | };
15 |
16 | return Mock;
17 | }
18 | }
19 | );
20 |
--------------------------------------------------------------------------------
/@test/src/components/@shared/Footer.test.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React from "react";
3 | import { shallow } from "enzyme";
4 | import toJson from "enzyme-to-json";
5 | // Module
6 | import Footer from "@/components/@shared/Footer";
7 |
8 | describe("Component Footer", () => {
9 | it('should render correctly in "debug" mode', () => {
10 | const component = shallow();
11 | expect(toJson(component)).toMatchSnapshot();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/@test/src/components/@shared/Header.test.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React from "react";
3 | import { shallow } from "enzyme";
4 | import toJson from "enzyme-to-json";
5 | // Module
6 | import Header from "@/components/@shared/Header";
7 |
8 | describe("Component Header", () => {
9 | it('should render correctly in "debug" mode', () => {
10 | const component = shallow();
11 | expect(toJson(component)).toMatchSnapshot();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React from "react";
3 | import { render } from "react-dom";
4 | // Root
5 | import Root from "@/routes/Root";
6 | // Styles
7 | import "@/themes/App.global.scss";
8 |
9 | const App = Root;
10 | render(, document.getElementById("app"));
11 |
12 | if (module.hot) {
13 | module.hot.accept("./routes/Root", () => {
14 | require("@/routes/Root");
15 | render(, document.getElementById("app"));
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/@test/src/components/Home/HelloWorld.test.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React from "react";
3 | import { shallow } from "enzyme";
4 | import toJson from "enzyme-to-json";
5 | // Module
6 | import HelloWorld from "@/components/Home/HelloWorld";
7 |
8 | describe("Component HelloWorld", () => {
9 | it('should render correctly in "debug" mode', () => {
10 | const component = shallow();
11 | expect(toJson(component)).toMatchSnapshot();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/components/Home/HelloWorld.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React, { Component } from "react";
3 | // Styles
4 | import styles from "./HelloWorld.scss";
5 |
6 | /**
7 | * HelloWorld
8 | *
9 | * @class HelloWorld
10 | * @extends {Component}
11 | */
12 | class HelloWorld extends Component {
13 | constructor(props) {
14 | super(props);
15 | }
16 |
17 | render() {
18 | return Hello World!
;
19 | }
20 | }
21 |
22 | export default HelloWorld;
23 |
--------------------------------------------------------------------------------
/@test/src/layouts/App.test.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React from "react";
3 | import { shallow } from "enzyme";
4 | import toJson from "enzyme-to-json";
5 |
6 | // Module
7 | import LayoutApp from "@/layouts/App";
8 |
9 | describe("Layout LayoutApp", () => {
10 | it('should render correctly in "debug" mode', () => {
11 | const component = shallow(
12 |
13 | children
14 |
15 | );
16 | expect(toJson(component)).toMatchSnapshot();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-cssnext": {
4 | browsers: [
5 | "Firefox >= 58",
6 | "Chrome >= 62",
7 | "ie >= 10",
8 | "last 4 versions",
9 | "Safari >= 9"
10 | ]
11 | },
12 | "postcss-import": {},
13 | "postcss-pxtorem": {
14 | rootValue: 16,
15 | unitPrecision: 5,
16 | propList: ["*"],
17 | selectorBlackList: ["html", "body"],
18 | replace: true,
19 | mediaQuery: false,
20 | minPixelValue: 0
21 | },
22 | "postcss-nested": {}
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/src/components/Home/Path.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React, { Component } from "react";
3 | import { Breadcrumb } from "antd";
4 | // Styles
5 | import styles from "./Path.scss";
6 |
7 | /**
8 | * Path
9 | *
10 | * @class Path
11 | * @extends {Component}
12 | */
13 | class Path extends Component {
14 | constructor(props) {
15 | super(props);
16 | }
17 |
18 | render() {
19 | return (
20 |
21 | Home
22 | List
23 | App
24 |
25 | );
26 | }
27 | }
28 |
29 | export default Path;
30 |
--------------------------------------------------------------------------------
/src/components/@shared/Footer.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React, { Component } from "react";
3 | import { Layout } from "antd";
4 | // Styles
5 | import styles from "./Footer.scss";
6 | // Config
7 | import { IMAGES } from "@/config";
8 |
9 | /**
10 | * Footer
11 | *
12 | * @class Footer
13 | * @extends {Component}
14 | */
15 | class Footer extends Component {
16 | constructor(props) {
17 | super(props);
18 | }
19 |
20 | render() {
21 | return (
22 |
23 |
24 | electron-react-ant-boilerplate ©2019
25 |
26 | );
27 | }
28 | }
29 |
30 | export default Footer;
31 |
--------------------------------------------------------------------------------
/src/screens/Home/Home.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React, { Component } from "react";
3 | // Styles
4 | import styles from "./Home.scss";
5 | // Layouts
6 | import Layout from "@/layouts/App";
7 | // Components
8 | import Path from "@/components/Home/Path";
9 | import HelloWorld from "@/components/Home/HelloWorld";
10 |
11 | /**
12 | * Home
13 | *
14 | * @class Home
15 | * @extends {Component}
16 | */
17 | class Home extends Component {
18 | constructor(props) {
19 | super(props);
20 | }
21 |
22 | render() {
23 | return (
24 |
25 |
29 |
30 | );
31 | }
32 | }
33 |
34 | export default Home;
35 |
--------------------------------------------------------------------------------
/src/themes/Ant.vars.scss:
--------------------------------------------------------------------------------
1 | $primary-color: #1890ff; // primary color for all components
2 | $link-color: #1890ff; // link color
3 | $success-color: #52c41a; // success state color
4 | $warning-color: #faad14; // warning state color
5 | $error-color: #f5222d; // error state color
6 | $font-size-base: 14px; // major text font size
7 | $heading-color: rgba(0, 0, 0, 0.85); // heading text color
8 | $text-color: rgba(0, 0, 0, 0.65); // major text color
9 | $text-color-secondary: rgba(0, 0, 0, 0.45); // secondary text color
10 | $disabled-color: rgba(0, 0, 0, 0.25); // disable state color
11 | $border-radius-base: 4px; // major border radius
12 | $border-color-base: #d9d9d9; // major border color
13 | $box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // major shadow for layers
14 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["react"],
3 | "parser": "babel-eslint",
4 | "parserOptions": {
5 | "ecmaVersion": 6,
6 | "sourceType": "module",
7 | "ecmaFeatures": {
8 | "jsx": true
9 | }
10 | },
11 | "env": {
12 | "es6": true,
13 | "browser": true,
14 | "node": true,
15 | "mocha": true
16 | },
17 | "extends": ["eslint:recommended", "plugin:react/recommended"],
18 | "rules": {
19 | "no-console": "off",
20 | "no-undef": "warn",
21 | "no-undefined": "warn",
22 | "no-unused-vars": "warn",
23 | "no-extra-parens": ["error", "all", { "ignoreJSX": "all" }],
24 | "no-constant-condition": "warn",
25 | "react/prop-types": "warn"
26 | },
27 | "settings": {
28 | "react": {
29 | "version": "16"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/env", "@babel/react"],
3 | "plugins": [
4 | [
5 | "import",
6 | {
7 | "libraryName": "antd",
8 | "style": true
9 | },
10 | "ant"
11 | ],
12 | "@babel/plugin-proposal-class-properties",
13 | "@babel/transform-runtime",
14 | "react-hot-loader/babel",
15 | [
16 | "babel-plugin-root-import",
17 |
18 | {
19 | "rootPathPrefix": "@",
20 | "rootPathSuffix": "./src"
21 | }
22 | ]
23 | ],
24 | "env": {
25 | "development": {
26 | "plugins": ["@babel/plugin-transform-modules-commonjs"]
27 | },
28 | "test": {
29 | "plugins": ["@babel/plugin-transform-modules-commonjs"]
30 | },
31 | "production": {
32 | "plugins": ["transform-react-remove-prop-types"]
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/@shared/Header.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React, { Component } from "react";
3 | import { Layout, Menu } from "antd";
4 | // Styles
5 | import styles from "./Header.scss";
6 |
7 | /**
8 | * Header
9 | *
10 | * @class Header
11 | * @extends {Component}
12 | */
13 | class Header extends Component {
14 | constructor(props) {
15 | super(props);
16 | }
17 |
18 | render() {
19 | return (
20 |
21 |
22 |
32 |
33 | );
34 | }
35 | }
36 |
37 | export default Header;
38 |
--------------------------------------------------------------------------------
/src/layouts/App.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import React, { Component } from "react";
3 | import PropTypes from "prop-types";
4 | import { Layout } from "antd";
5 | // Styles
6 | import styles from "./App.scss";
7 | // Components
8 | import Header from "@/components/@shared/Header";
9 | import Footer from "@/components/@shared/Footer";
10 |
11 | /**
12 | * App
13 | *
14 | * @class App
15 | * @extends {Component}
16 | */
17 | class App extends Component {
18 | static propTypes = {
19 | children: PropTypes.node.isRequired
20 | };
21 |
22 | constructor(props) {
23 | super(props);
24 | }
25 |
26 | render() {
27 | const { children } = this.props;
28 | return (
29 |
30 |
31 |
32 | {children}
33 |
34 |
35 |
36 | );
37 | }
38 | }
39 |
40 | export default App;
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build folder and files #
2 | ##########################
3 | release/
4 |
5 | # Development folders and files #
6 | #################################
7 | .tmp/
8 | dist/
9 | node_modules/
10 | docs/
11 | __snapshots__
12 | *.compiled.*
13 |
14 | # Folder config file #
15 | ######################
16 | Desktop.ini
17 |
18 | # Folder notes #
19 | ################
20 | _ignore/
21 |
22 | # Log files & folders #
23 | #######################
24 | logs/
25 | *.log
26 | npm-debug.log*
27 | .npm
28 |
29 | # Packages #
30 | ############
31 | # it's better to unpack these files and commit the raw source
32 | # git has its own built in compression methods
33 | *.7z
34 | *.dmg
35 | *.gz
36 | *.iso
37 | *.jar
38 | *.rar
39 | *.tar
40 | *.zip
41 |
42 | # Photoshop & Illustrator files #
43 | #################################
44 | *.ai
45 | *.eps
46 | *.psd
47 |
48 | # Windows & Mac file caches #
49 | #############################
50 | .DS_Store
51 | Thumbs.db
52 | ehthumbs.db
53 |
54 | # Windows shortcuts #
55 | #####################
56 | *.lnk
57 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 leonardo Rico
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # electron-react-ant-boilerplate
2 |
3 | [](https://npmjs.org/package/electron-react-ant-boilerplate) [](https://raw.githubusercontent.com/kevoj/electron-react-ant-boilerplate/master/LICENSE)
4 |
5 | 
6 |
7 | ### Features
8 |
9 | - Electron
10 | - Webpack 4
11 | - Babel 7
12 | - React 16
13 | - ES6
14 | - PostCSS
15 | - Sass (Injection by modules and in the traditional way)
16 | - Ant Design (Global theme based on the Less Ant variables)
17 | - Jest
18 | - Enzyme
19 | - Eslint
20 | - Hot realod
21 | - Friendly architecture
22 | - Export for Mac, Linux, Windows
23 |
24 |
25 | ### Table of contents
26 |
27 | * [Install](#install)
28 | * [Usage](#usage)
29 | * [License](#license)
30 |
31 | ### Install
32 |
33 | #### Clone this repo
34 |
35 | ```
36 | git clone https://github.com/kevoj/electron-react-ant-boilerplate.git
37 | ```
38 |
39 | #### Install dependencies
40 |
41 | ```
42 | npm install
43 | ```
44 | or
45 | ```
46 | yarn
47 | ```
48 |
49 | ### Usage
50 |
51 | #### Run
52 |
53 | ```
54 | npm start
55 | ```
56 | or
57 | ```
58 | yarn start
59 | ```
60 |
61 | #### Build (manual)
62 |
63 | ```
64 | npm run build
65 | ```
66 | or
67 | ```
68 | yarn build
69 | ```
70 |
71 | #### Prod (Preview in Production)
72 |
73 | ```
74 | npm run prod
75 | ```
76 | or
77 | ```
78 | yarn prod
79 | ```
80 |
81 | #### Build package (Current OS)
82 |
83 | ```
84 | npm run package
85 | ```
86 | or
87 | ```
88 | yarn package
89 | ```
90 |
91 | #### Build package (Mac, Linux, Windows)
92 |
93 | ```
94 | npm run package:all
95 | ```
96 | or
97 | ```
98 | yarn package:all
99 | ```
100 |
101 | #### Test
102 |
103 | ```
104 | npm test
105 | ```
106 | or
107 | ```
108 | yarn test
109 | ```
110 |
111 | #### Docs
112 |
113 | ```
114 | npm run docs
115 | ```
116 | or
117 | ```
118 | yarn docs
119 | ```
120 |
121 | ### License
122 |
123 | MIT © [Leonardo Rico](https://github.com/kevoj/electron-react-ant-boilerplate/blob/master/LICENSE)
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Import parts of electron to use
4 | const { app, BrowserWindow, Menu } = require("electron");
5 | const path = require("path");
6 | const url = require("url");
7 | const isDev = require("electron-is-dev");
8 |
9 | // Keep a global reference of the window object, if you don't, the window will
10 | // be closed automatically when the JavaScript object is garbage collected.
11 | let mainWindow;
12 |
13 | // Temporary fix broken high-dpi scale factor on Windows (125% scaling)
14 | // info: https://github.com/electron/electron/issues/9691
15 | if (process.platform === "win32") {
16 | app.commandLine.appendSwitch("high-dpi-support", "true");
17 | app.commandLine.appendSwitch("force-device-scale-factor", "1");
18 | }
19 |
20 | function createWindow() {
21 | // Create the browser window.
22 | mainWindow = new BrowserWindow({
23 | width: 1024,
24 | height: 768,
25 | show: false
26 | });
27 |
28 | // and load the index.html of the app.
29 | let indexPath;
30 |
31 | if (isDev && process.argv.indexOf("--noDevServer") === -1) {
32 | indexPath = url.format({
33 | protocol: "http:",
34 | host: "localhost:3100",
35 | pathname: "index.html",
36 | slashes: true
37 | });
38 | } else {
39 | indexPath = url.format({
40 | protocol: "file:",
41 | pathname: path.join(__dirname, "dist", "index.html"),
42 | slashes: true
43 | });
44 | }
45 |
46 | mainWindow.loadURL(indexPath);
47 |
48 | // Don't show until we are ready and loaded
49 | mainWindow.once("ready-to-show", () => {
50 | mainWindow.show();
51 |
52 | // Open the DevTools automatically if developing
53 | if (isDev) {
54 | mainWindow.webContents.openDevTools();
55 |
56 | mainWindow.webContents.on("context-menu", (e, props) => {
57 | const { x, y } = props;
58 |
59 | Menu.buildFromTemplate([
60 | {
61 | label: "Inspect element",
62 | click: () => {
63 | mainWindow.inspectElement(x, y);
64 | }
65 | }
66 | ]).popup(mainWindow);
67 | });
68 | }
69 | });
70 |
71 | // Emitted when the window is closed.
72 | mainWindow.on("closed", function() {
73 | // Dereference the window object, usually you would store windows
74 | // in an array if your app supports multi windows, this is the time
75 | // when you should delete the corresponding element.
76 | mainWindow = null;
77 | });
78 | }
79 |
80 | // This method will be called when Electron has finished
81 | // initialization and is ready to create browser windows.
82 | // Some APIs can only be used after this event occurs.
83 | app.on("ready", createWindow);
84 |
85 | // Quit when all windows are closed.
86 | app.on("window-all-closed", () => {
87 | // On macOS it is common for applications and their menu bar
88 | // to stay active until the user quits explicitly with Cmd + Q
89 | if (process.platform !== "darwin") {
90 | app.quit();
91 | }
92 | });
93 |
94 | app.on("activate", () => {
95 | // On macOS it's common to re-create a window in the app when the
96 | // dock icon is clicked and there are no other windows open.
97 | if (mainWindow === null) {
98 | createWindow();
99 | }
100 | });
101 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "electron-react-ant-boilerplate",
3 | "version": "1.0.9",
4 | "description": "Electron, Webpack4, React, Babel 7, ES6, PostCSS, Sass, Ant Design",
5 | "license": "MIT",
6 | "private": false,
7 | "repository": {
8 | "type": "git",
9 | "url": "git+https://github.com/kevoj/electron-react-ant-boilerplate.git"
10 | },
11 | "author": {
12 | "name": "leonardo R",
13 | "email": "leonardo.ricog@gmail.com",
14 | "url": "https://github.com/kevoj"
15 | },
16 | "keywords": [
17 | "react",
18 | "electron",
19 | "boilerplate",
20 | "webpack",
21 | "react-electron",
22 | "react-electron-webpack",
23 | "electron-boilerplate",
24 | "react-boilerplate",
25 | "react-ant-boilerplate",
26 | "react-ant",
27 | "ant-boilerplate",
28 | "ant-design",
29 | "react-ant-design",
30 | "react-ant-design-boilerplate",
31 | "react-webpack",
32 | "ant-webpack",
33 | "react-babel",
34 | "webpack",
35 | "es6",
36 | "babel-7",
37 | "less",
38 | "sass"
39 | ],
40 | "engines": {
41 | "node": ">=8.0.0",
42 | "npm": ">=5.0.0",
43 | "yarn": ">=1.0.0"
44 | },
45 | "homepage": "",
46 | "main": "main.js",
47 | "scripts": {
48 | "start": "npm run env:dev -- webpack-dev-server",
49 | "build": "npm run env:prod -- webpack",
50 | "prod": "npm run build && electron --noDevServer .",
51 | "package": "npm run build",
52 | "package:all": "npm run build",
53 | "test": "jest -u",
54 | "lint": "eslint ./src",
55 | "docs": "jsdoc -r ./src -d docs --verbose",
56 | "env:dev": "cross-env NODE_ENV=development",
57 | "env:prod": "cross-env NODE_ENV=production",
58 | "build:clean": "rimraf -rf dist",
59 | "build:copy": "copyfiles -u 1 public/* public/**/* dist -e public/index.html",
60 | "prebuild": "npm run build:clean && npm run build:copy",
61 | "prepackage:all": "rimraf -rf release",
62 | "postpackage": "electron-builder build",
63 | "postpackage:all": "electron-builder build -mwl"
64 | },
65 | "dependencies": {
66 | "antd": "^3.16.3",
67 | "classnames": "^2.2.6",
68 | "electron-is-dev": "^1.1.0",
69 | "prop-types": "^15.7.2",
70 | "react": "^16.8.6",
71 | "react-dom": "^16.8.6",
72 | "react-router-dom": "^5.0.0"
73 | },
74 | "devDependencies": {
75 | "@babel/core": "^7.4.3",
76 | "@babel/plugin-proposal-class-properties": "^7.4.0",
77 | "@babel/plugin-transform-modules-commonjs": "^7.4.3",
78 | "@babel/plugin-transform-runtime": "^7.4.3",
79 | "@babel/preset-env": "^7.4.3",
80 | "@babel/preset-react": "^7.0.0",
81 | "@babel/register": "^7.4.0",
82 | "@hot-loader/react-dom": "^16.8.6",
83 | "antd-scss-theme-plugin": "^1.0.7",
84 | "babel-eslint": "^10.0.1",
85 | "babel-jest": "^24.7.1",
86 | "babel-loader": "^8.0.5",
87 | "babel-plugin-import": "^1.11.0",
88 | "babel-plugin-root-import": "^6.1.0",
89 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
90 | "babili-webpack-plugin": "^0.1.2",
91 | "brotli-webpack-plugin": "^1.1.0",
92 | "copyfiles": "^2.1.0",
93 | "cross-env": "^5.2.0",
94 | "css-hot-loader": "^1.4.4",
95 | "css-loader": "^2.1.1",
96 | "electron": "^4.1.4",
97 | "electron-builder": "^20.39.0",
98 | "enzyme": "^3.9.0",
99 | "enzyme-adapter-react-16": "^1.12.1",
100 | "enzyme-to-json": "^3.3.5",
101 | "eslint": "^5.16.0",
102 | "eslint-loader": "^2.1.2",
103 | "eslint-plugin-react": "^7.12.4",
104 | "file-loader": "^3.0.1",
105 | "html-webpack-plugin": "^3.2.0",
106 | "jest": "^24.7.1",
107 | "jest-cli": "^24.7.1",
108 | "jsdoc": "^3.5.5",
109 | "less": "^3.9.0",
110 | "less-loader": "^4.1.0",
111 | "mini-css-extract-plugin": "^0.6.0",
112 | "node-sass": "^4.11.0",
113 | "optimize-css-assets-webpack-plugin": "^5.0.1",
114 | "postcss-import": "^12.0.1",
115 | "postcss-loader": "^3.0.0",
116 | "postcss-nested": "^4.1.2",
117 | "postcss-preset-env": "^6.6.0",
118 | "postcss-pxtorem": "^4.0.1",
119 | "react-hot-loader": "^4.8.4",
120 | "rimraf": "^2.6.3",
121 | "sass-loader": "^7.1.0",
122 | "style-loader": "^0.23.1",
123 | "thread-loader": "^2.1.2",
124 | "uglifyjs-webpack-plugin": "^2.1.2",
125 | "webpack": "^4.30.0",
126 | "webpack-cli": "^3.3.0",
127 | "webpack-dev-server": "^3.3.1",
128 | "webpack-merge": "^4.2.1"
129 | },
130 | "build": {
131 | "productName": "MyApp",
132 | "appId": "org.develar.MyApp",
133 | "files": [
134 | "dist",
135 | "main.js",
136 | "package.json"
137 | ],
138 | "dmg": {
139 | "contents": [
140 | {
141 | "x": 130,
142 | "y": 220
143 | },
144 | {
145 | "x": 410,
146 | "y": 220,
147 | "type": "link",
148 | "path": "/Applications"
149 | }
150 | ]
151 | },
152 | "win": {
153 | "target": [
154 | "nsis",
155 | "msi"
156 | ]
157 | },
158 | "linux": {
159 | "target": [
160 | "deb",
161 | "rpm",
162 | "snap",
163 | "AppImage"
164 | ],
165 | "category": "Development"
166 | },
167 | "directories": {
168 | "buildResources": "resources",
169 | "output": "release"
170 | }
171 | },
172 | "jest": {
173 | "snapshotSerializers": [
174 | "enzyme-to-json/serializer"
175 | ],
176 | "setupFiles": [
177 | "raf/polyfill",
178 | "/@test/config/setupTests.js"
179 | ],
180 | "moduleNameMapper": {
181 | "^.+\\.(css|less|scss)$": "babel-jest",
182 | "^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/@test/config/fileMock.js",
183 | "^components$": "/@test/config/componentsMock.js"
184 | },
185 | "moduleDirectories": [
186 | "src",
187 | "@test/src",
188 | "node_modules"
189 | ]
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/webpack.config.babel.js:
--------------------------------------------------------------------------------
1 | import { spawn } from "child_process";
2 | import path from "path";
3 | import webpack from "webpack";
4 | import merge from "webpack-merge";
5 | import HtmlWebpackPlugin from "html-webpack-plugin";
6 | import postcssPresetEnv from "postcss-preset-env";
7 | import AntdScssThemePlugin from "antd-scss-theme-plugin";
8 | import BabiliPlugin from "babili-webpack-plugin";
9 | import TerserPlugin from "terser-webpack-plugin";
10 | import UglifyJsPlugin from "uglifyjs-webpack-plugin";
11 | import BrotliPlugin from "brotli-webpack-plugin";
12 | import MiniCssExtractPlugin from "mini-css-extract-plugin";
13 | import OptimizeCSSAssetsPlugin from "optimize-css-assets-webpack-plugin";
14 |
15 | const host = `0.0.0.0`;
16 | const port = 3100;
17 | const src = path.resolve(__dirname, "src");
18 |
19 | const isDev = process.env.NODE_ENV === "development";
20 |
21 | const cssModuleLoader = {
22 | loader: "css-loader",
23 | options: {
24 | importLoaders: 2,
25 | modules: true,
26 | camelCase: true,
27 | sourceMap: isDev,
28 | localIdentName: isDev
29 | ? "[folder]__[name]__[local]__[hash:base64:5]"
30 | : "[hash:base64:5]"
31 | }
32 | };
33 |
34 | const cssLoader = {
35 | loader: "css-loader",
36 | options: {
37 | importLoaders: 2,
38 | modules: false,
39 | sourceMap: isDev
40 | }
41 | };
42 |
43 | const postCssLoader = {
44 | loader: "postcss-loader",
45 | options: {
46 | ident: "postcss",
47 | sourceMap: isDev,
48 | plugins: () => [postcssPresetEnv()]
49 | }
50 | };
51 |
52 | const sassLoader = {
53 | loader: "sass-loader",
54 | options: {
55 | sourceMap: isDev
56 | }
57 | };
58 |
59 | const lessLoader = AntdScssThemePlugin.themify({
60 | loader: "less-loader",
61 | options: {
62 | sourceMap: isDev,
63 | javascriptEnabled: true
64 | }
65 | });
66 |
67 | const sassHotLoader = {
68 | loader: "css-hot-loader"
69 | };
70 |
71 | const sassHotModuleLoader = {
72 | loader: "css-hot-loader",
73 | options: {
74 | cssModule: true
75 | }
76 | };
77 |
78 | const assetsLoader = {
79 | loader: "file-loader?name=[name]__[hash:base64:5].[ext]"
80 | };
81 |
82 | const babelLoader = [
83 | {
84 | loader: "thread-loader"
85 | },
86 | {
87 | loader: "babel-loader",
88 | options: {
89 | cacheDirectory: true
90 | }
91 | }
92 | ];
93 |
94 | const babelDevLoader = babelLoader.concat([
95 | "react-hot-loader/webpack",
96 | "eslint-loader"
97 | ]);
98 |
99 | const config = {
100 | target: "electron-renderer",
101 | base: {
102 | module: {
103 | rules: [
104 | {
105 | test: /\.(js|jsx)$/,
106 | use: isDev ? babelDevLoader : babelLoader,
107 | exclude: /node_modules/
108 | },
109 | {
110 | test: /\.(jpe?g|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$/,
111 | use: [assetsLoader]
112 | },
113 | {
114 | test: /\.global|vars\.scss$/,
115 | use: [
116 | sassHotLoader,
117 | MiniCssExtractPlugin.loader,
118 | cssLoader,
119 | postCssLoader,
120 | sassLoader
121 | ]
122 | },
123 | {
124 | test: /\.scss$/,
125 | exclude: /\.global|vars\.scss$/,
126 | use: [
127 | sassHotModuleLoader,
128 | MiniCssExtractPlugin.loader,
129 | cssModuleLoader,
130 | postCssLoader,
131 | sassLoader
132 | ]
133 | },
134 | {
135 | test: /\.less$/,
136 | use: [
137 | sassHotLoader,
138 | MiniCssExtractPlugin.loader,
139 | cssLoader,
140 | lessLoader
141 | ]
142 | }
143 | ]
144 | },
145 | plugins: [
146 | new webpack.DefinePlugin({
147 | NODE_ENV: process.env.NODE_ENV
148 | }),
149 | new AntdScssThemePlugin(
150 | path.join(__dirname, "src", "themes/Ant.vars.scss")
151 | ),
152 | new MiniCssExtractPlugin({
153 | filename: isDev ? "[name].css" : "[name].[chunkhash].css",
154 | chunkFilename: isDev ? "[id].css" : "[name].[chunkhash].css",
155 | reload: false
156 | }),
157 | new HtmlWebpackPlugin({
158 | template: "public/index.html",
159 | minify: {
160 | collapseWhitespace: !isDev
161 | }
162 | })
163 | ]
164 | },
165 | development: {
166 | mode: "development",
167 | plugins: [new webpack.HotModuleReplacementPlugin()],
168 | entry: [
169 | "react-hot-loader/patch",
170 | "webpack/hot/only-dev-server",
171 | src
172 | ],
173 | devtool: "cheap-module-source-map",
174 | cache: true,
175 | devServer: {
176 | host,
177 | port,
178 | hot: true,
179 | contentBase: "public",
180 | compress: true,
181 | inline: true,
182 | lazy: false,
183 | stats: "errors-only",
184 | historyApiFallback: {
185 | verbose: true,
186 | disableDotRule: false
187 | },
188 | headers: { "Access-Control-Allow-Origin": "*" },
189 | stats: {
190 | colors: true,
191 | chunks: false,
192 | children: false
193 | },
194 | before() {
195 | spawn("electron", ["."], {
196 | shell: true,
197 | env: process.env,
198 | stdio: "inherit"
199 | })
200 | .on("close", code => process.exit(0))
201 | .on("error", spawnError => console.error(spawnError));
202 | }
203 | },
204 | optimization: {
205 | namedModules: true
206 | },
207 | resolve: {
208 | extensions: [".js", ".jsx", ".json"],
209 | modules: [].concat(src, ["node_modules"]),
210 | alias: {
211 | "react-dom": "@hot-loader/react-dom"
212 | }
213 | }
214 | },
215 | production: {
216 | mode: "production",
217 | entry: {
218 | app: src
219 | },
220 | plugins: [
221 | new BrotliPlugin({
222 | asset: "[path].br[query]",
223 | test: /\.(js|css|html|svg)$/,
224 | threshold: 10240,
225 | minRatio: 0.8
226 | })
227 | ],
228 | output: {
229 | path: __dirname + "/dist",
230 | filename: "[name].[chunkhash].js"
231 | },
232 | optimization: {
233 | minimizer: [
234 | new UglifyJsPlugin(),
235 | new TerserPlugin(),
236 | new BabiliPlugin(),
237 | new OptimizeCSSAssetsPlugin()
238 | ],
239 | splitChunks: {
240 | cacheGroups: {
241 | commons: {
242 | test: /[\\/]node_modules[\\/]/,
243 | name: "vendors",
244 | chunks: "all"
245 | }
246 | }
247 | }
248 | }
249 | }
250 | };
251 |
252 | export default merge(config.base, config[process.env.NODE_ENV]);
253 |
--------------------------------------------------------------------------------