rebuiltron 5.0.0 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ A tool made to easily build an offline **React/Electron** app using webpack.
9
9
  The idea behind Rebuiltron was to migrate one of my project initially created with the deprecated [CRA](https://create-react-app.dev/) to a maintained tool configured with Electron in mind. As such, it has been developed using [react-scripts](https://github.com/facebook/create-react-app/tree/main/packages/react-scripts) as a base, but heavily edited and stripped of a lot of features.
10
10
 
11
11
  > [!IMPORTANT]
12
- > Since I made Rebuiltron specifically for one of my own projects, I only kept in the configuration what *this* project needed and nothing more. Except for the entry points and some SASS options, **Rebuiltron doesn't offer any configurable options**. If you are looking to create a new Electron/React app, or even migrating an existing CRA app, you should probably take a look at [electron-vite](https://electron-vite.org/) instead.
12
+ > Since I made Rebuiltron specifically for one of my own projects, I only kept in the configuration what *this* project needed and nothing more. Except for the entry points and some basic options, **Rebuiltron doesn't offer many configurable options**. If you are looking to create a new Electron/React app, or even migrating an existing CRA app, you should probably look for a more generic tool.
13
13
 
14
14
 
15
15
  ## Features
@@ -20,10 +20,10 @@ Rebuiltron uses webpack with [SWC](https://swc.rs/) to compile JavaScript instea
20
20
  - Development server taking care of starting Electron
21
21
  - Production bundler for React and Electron code
22
22
  - Support for React, JSX, SASS, ICSS
23
- - Support for ESM
23
+ - Support for ES6 imports on all processes
24
24
 
25
25
  > [!WARNING]
26
- > Rebuiltron **doesn't support**: TypeScript, Flow, CSS Modules, Jest, and proxying.
26
+ > Rebuiltron **doesn't support**: TypeScript, Flow, CSS Modules, ESM, Jest, and proxying.
27
27
 
28
28
 
29
29
  ## Installation
@@ -36,7 +36,7 @@ yarn add rebuiltron -D
36
36
 
37
37
  ## Configuration
38
38
 
39
- The following documentation assume you already have a basic knowledge of how to use React and Electron.
39
+ The following documentation assumes you already have a basic knowledge of how to use React and Electron.
40
40
 
41
41
  ### Folder structure
42
42
 
@@ -56,12 +56,14 @@ Set the main Electron entry in `package.json`:
56
56
 
57
57
  ```json
58
58
  {
59
- "main": "electron/main.js",
59
+ "main": "build/static/js/electron.main.js",
60
60
  }
61
61
  ```
62
62
 
63
+ This field points to the compiled version of the main file and **must** be named as above. Your original main file can be named however you want.
64
+
63
65
  ### Scripts
64
- Add Rebuiltron scripts:
66
+ Add Rebuiltron scripts to your `package.json`:
65
67
 
66
68
  ```json
67
69
  {
@@ -84,12 +86,13 @@ Add your desired [browserslist](https://github.com/browserslist/browserslist) in
84
86
 
85
87
  ### Configuration file
86
88
 
87
- At the root of your project, create a `rebuiltron.config.cjs` file.
89
+ At the root of your project, create a `rebuiltron.config.js` file.
88
90
 
89
91
  #### Options:
90
92
 
91
- | Option | Type | Required | Description |
93
+ | Name | Type | Required | Description |
92
94
  | --- | :---: | :---: | --- |
95
+ | `main` | `string` | ✓ | Main entry. The path must be relative.
93
96
  | `renderers` | `object` | ✓ | Renderer entries. It takes the name of the entries as keys and their paths as values. All paths must be relative. |
94
97
  | `preloads` | `object` | ✓ | Preload entries. It takes the name of the entries as keys and their paths as values. All paths must be relative. |
95
98
  | `srcAlias` | `string` | ✗ | Custom [alias](https://webpack.js.org/configuration/resolve/#resolvealias) to the `src` folder.
@@ -104,12 +107,13 @@ At the root of your project, create a `rebuiltron.config.cjs` file.
104
107
 
105
108
  ```js
106
109
  module.exports = {
110
+ main: "./electron/main.js",
107
111
  renderers: {
108
112
  app: "./src/app.js",
109
113
  worker: "./src/worker.js"
110
114
  },
111
115
  preloads: {
112
- worker: "./electron/preloads/worker.js"
116
+ app: "./electron/preloads/app.js"
113
117
  },
114
118
  srcAlias: "@app",
115
119
  sassOptions: {
@@ -124,14 +128,27 @@ module.exports = {
124
128
  > [!NOTE]
125
129
  > Your renderers entry file(s) will be automatically injected into your HTML file(s). Thus, make sure you have a corresponding HTML file in the `public` folder for each entry (using the same name).
126
130
 
131
+ ### Preloads
132
+
133
+ Since Rebuiltron will build the preload file(s) both in development and production, you can use a single path for both environment. Built files use the names declared in your configuration: `electron.preload.[name].js`.
134
+
135
+ ```js
136
+ const appWindow = new BrowserWindow({
137
+ webPreferences: {
138
+ // Using the example configuration file above
139
+ preload: path.join(__dirname, "electron.preload.app.js"),
140
+ }
141
+ });
142
+ ```
143
+
127
144
  ### Environment variables
128
145
 
129
146
  When the development server is running, Rebuiltron exposes a `DEV_LOCAL_URL` variable that you can access on your main process using `process.env.DEV_LOCAL_URL`.
130
147
 
131
148
  ```js
132
- if(!app.isPackaged) {
133
- appWindow.loadURL(`${process.env.DEV_LOCAL_URL}/index.html`);
134
- }
149
+ app.isPackaged
150
+ ? appWindow.loadFile(join(__dirname, "../../app.html")) // Prod
151
+ : appWindow.loadURL(`${process.env.DEV_LOCAL_URL}/app.html`); // Dev
135
152
  ```
136
153
 
137
154
  ## Usage
@@ -1,22 +1,12 @@
1
1
  const { createHash } = require("crypto");
2
2
 
3
- const HtmlWebpackPlugin = require("html-webpack-plugin");
4
3
  const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
5
- const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin");
6
4
  const TerserPlugin = require("terser-webpack-plugin");
7
- const MiniCssExtractPlugin = require("mini-css-extract-plugin");
8
- const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
9
5
  const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin");
10
- const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
11
- const CopyPlugin = require("copy-webpack-plugin");
12
6
 
13
7
  const paths = require("../helpers/paths");
14
- const { shouldUseSourceMap, isEnvDevelopment, isEnvProduction } = require("../helpers/environment");
8
+ const { isEnvDevelopment, isEnvProduction } = require("../helpers/environment");
15
9
  const { emptyOr } = require("../helpers/utils");
16
- const imageLoaders = require("../loaders/images");
17
- const styleLoaders = require("../loaders/style");
18
- const javascriptLoaders = require("../loaders/javascript");
19
- const rebuiltronConfig = require("../rebuiltronConfig");
20
10
 
21
11
 
22
12
  const { srcAlias } = require(paths.appConfig);
@@ -72,53 +62,14 @@ module.exports = {
72
62
  comments: false
73
63
  }
74
64
  }
75
- }),
76
- new CssMinimizerPlugin()
65
+ })
77
66
  ]
78
67
  },
79
68
  module: {
80
- strictExportPresence: true,
81
- rules: [
82
- // Handles `node_modules` packages that contain sourcemaps
83
- ...emptyOr(shouldUseSourceMap, [{
84
- enforce: "pre",
85
- exclude: /@babel(?:\/|\\{1,2})runtime/,
86
- test: /\.(js|mjs|jsx|ts|tsx|css)$/,
87
- loader: require.resolve("source-map-loader")
88
- }]),
89
- {
90
- oneOf: [
91
- ...imageLoaders,
92
- ...javascriptLoaders,
93
- ...styleLoaders,
94
- {
95
- // Makes sure the assets get served by `WebpackDevServer`
96
- exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
97
- type: "asset/resource"
98
- }
99
- ]
100
- }
101
- ]
69
+ strictExportPresence: true
102
70
  },
103
71
  plugins: [
104
- ...emptyOr(isEnvProduction, [
105
- new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]), // Injects scripts into HTML
106
- new MiniCssExtractPlugin({
107
- filename: `${rebuiltronConfig.buildDirs.css}/[name].[contenthash:8].css`,
108
- chunkFilename: `${rebuiltronConfig.buildDirs.css}/[name].[contenthash:8].chunk.css`
109
- }),
110
- new CopyPlugin({
111
- patterns: [{
112
- from: paths.appPublic,
113
- to: paths.appBuild,
114
- globOptions: {
115
- ignore: ["**/*.html"]
116
- }
117
- }]
118
- })
119
- ]),
120
72
  ...emptyOr(isEnvDevelopment, [
121
- new ReactRefreshWebpackPlugin({ overlay: false }),
122
73
  new CaseSensitivePathsPlugin() // Detects case errors in import paths
123
74
  ]),
124
75
  new ModuleNotFoundPlugin(paths.appPath) // Gives some necessary context to module not found errors
@@ -1,16 +1,28 @@
1
1
  const paths = require("../helpers/paths");
2
2
  const rebuiltronConfig = require("../rebuiltronConfig");
3
3
  const baseConfig = require("./base");
4
+ const javascriptLoaders = require("../loaders/javascript");
5
+
6
+
7
+ const { main } = require(paths.appConfig);
4
8
 
5
9
 
6
10
  module.exports = {
7
11
  ...baseConfig,
8
12
  target: "electron-main",
9
13
  entry: {
10
- [rebuiltronConfig.buildFileNames.main]: paths.electronMain
14
+ [rebuiltronConfig.buildFileNames.main]: main
11
15
  },
12
16
  output: {
13
17
  ...baseConfig.output,
14
18
  filename: `${rebuiltronConfig.buildDirs.js}/[name].js`
19
+ },
20
+ module: {
21
+ ...baseConfig.module,
22
+ rules: [
23
+ {
24
+ oneOf: javascriptLoaders
25
+ }
26
+ ]
15
27
  }
16
28
  };
@@ -3,6 +3,7 @@ const { mapKeys } = require("lodash");
3
3
  const paths = require("../helpers/paths");
4
4
  const rebuiltronConfig = require("../rebuiltronConfig");
5
5
  const baseConfig = require("./base");
6
+ const javascriptLoaders = require("../loaders/javascript");
6
7
 
7
8
 
8
9
  const { preloads } = require(paths.appConfig);
@@ -12,10 +13,18 @@ module.exports = {
12
13
  ...baseConfig,
13
14
  target: "electron-preload",
14
15
  entry: mapKeys(preloads, (_value, entryName) => (
15
- `${rebuiltronConfig.buildFileNames.preload}.${entryName}`)
16
- ),
16
+ `${rebuiltronConfig.buildFileNames.preload}.${entryName}`
17
+ )),
17
18
  output: {
18
19
  ...baseConfig.output,
19
20
  filename: `${rebuiltronConfig.buildDirs.js}/[name].js`
21
+ },
22
+ module: {
23
+ ...baseConfig.module,
24
+ rules: [
25
+ {
26
+ oneOf: javascriptLoaders
27
+ }
28
+ ]
20
29
  }
21
30
  };
@@ -1,13 +1,21 @@
1
1
  const path = require("path");
2
2
 
3
3
  const HtmlWebpackPlugin = require("html-webpack-plugin");
4
+ const CopyPlugin = require("copy-webpack-plugin");
5
+ const MiniCssExtractPlugin = require("mini-css-extract-plugin");
6
+ const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin");
7
+ const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
8
+ const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
4
9
  const { keys } = require("lodash");
5
10
 
6
11
  const paths = require("../helpers/paths");
7
- const { isEnvProduction, shouldUseSourceMap } = require("../helpers/environment");
12
+ const { isEnvProduction, isEnvDevelopment, shouldUseSourceMap } = require("../helpers/environment");
8
13
  const { emptyOr } = require("../helpers/utils");
9
14
  const rebuiltronConfig = require("../rebuiltronConfig");
10
15
  const baseConfig = require("./base");
16
+ const imageLoaders = require("../loaders/images");
17
+ const styleLoaders = require("../loaders/style");
18
+ const javascriptLoaders = require("../loaders/javascript");
11
19
 
12
20
 
13
21
  const { renderers } = require(paths.appConfig);
@@ -24,9 +32,59 @@ module.exports = {
24
32
  chunkFilename: `${rebuiltronConfig.buildDirs.js}/${isEnvProduction ? "[name].[contenthash:8].chunk.js" : "[name].chunk.js"}`,
25
33
  assetModuleFilename: `${rebuiltronConfig.buildDirs.media}/[name].[hash][ext]`
26
34
  },
35
+ optimization: {
36
+ ...baseConfig.optimization,
37
+ minimizer: [
38
+ ...baseConfig.optimization.minimizer,
39
+ new CssMinimizerPlugin()
40
+ ]
41
+ },
42
+ module: {
43
+ ...baseConfig.module,
44
+ rules: [
45
+ // Handles `node_modules` packages that contain sourcemaps
46
+ ...emptyOr(shouldUseSourceMap, [{
47
+ enforce: "pre",
48
+ exclude: /@babel(?:\/|\\{1,2})runtime/,
49
+ test: /\.(js|mjs|jsx|ts|tsx|css)$/,
50
+ loader: require.resolve("source-map-loader")
51
+ }]),
52
+ {
53
+ oneOf: [
54
+ ...imageLoaders,
55
+ ...javascriptLoaders,
56
+ ...styleLoaders,
57
+ {
58
+ // Makes sure the assets get served by `WebpackDevServer`
59
+ exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
60
+ type: "asset/resource"
61
+ }
62
+ ]
63
+ }
64
+ ]
65
+ },
27
66
  plugins: [
28
67
  ...baseConfig.plugins,
29
- ...keys(renderers).map(renderer => (
68
+ ...emptyOr(isEnvProduction, [
69
+ new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]), // Injects scripts into HTML
70
+ new MiniCssExtractPlugin({
71
+ filename: `${rebuiltronConfig.buildDirs.css}/[name].[contenthash:8].css`,
72
+ chunkFilename: `${rebuiltronConfig.buildDirs.css}/[name].[contenthash:8].chunk.css`
73
+ }),
74
+ new CopyPlugin({
75
+ patterns: [{
76
+ from: paths.appPublic,
77
+ to: paths.appBuild,
78
+ globOptions: {
79
+ ignore: ["**/*.html"]
80
+ }
81
+ }]
82
+ })
83
+ ]),
84
+ ...emptyOr(isEnvDevelopment, [
85
+ new ReactRefreshWebpackPlugin({ overlay: false })
86
+ ]),
87
+ ...keys(renderers).map((renderer) => (
30
88
  new HtmlWebpackPlugin({
31
89
  inject: true,
32
90
  template: path.join(paths.appPublic, `${renderer}.html`),
package/helpers/paths.js CHANGED
@@ -3,17 +3,11 @@ const { isEnvDevelopment } = require("./environment.js");
3
3
  const { resolveApp } = require("./utils.js");
4
4
 
5
5
 
6
- const packageJsonPath = resolveApp("package.json");
7
- const packageJson = require(packageJsonPath);
8
-
9
-
10
6
  module.exports = {
11
7
  appConfig: resolveApp(rebuiltronConfig.configFileName),
12
- electronMain: resolveApp(packageJson.main),
13
8
  appPath: resolveApp("."),
14
9
  appBuild: resolveApp(rebuiltronConfig.appDirs.build),
15
10
  appPublic: resolveApp(rebuiltronConfig.appDirs.public),
16
- appPackageJson: packageJsonPath,
17
11
  src: resolveApp(rebuiltronConfig.appDirs.src),
18
12
  appNodeModules: resolveApp("node_modules"),
19
13
  appWebpackCache: resolveApp("node_modules/.cache"),
@@ -3,17 +3,6 @@ const { shouldUseSourceMap, isEnvProduction } = require("../helpers/environment"
3
3
 
4
4
 
5
5
  module.exports = [
6
- {
7
- // Converts `import.meta.url` before SWC transpiles so paths using it stay dynamic
8
- test: /\.js$/,
9
- include: [paths.electronMain],
10
- loader: require.resolve("string-replace-loader"),
11
- options: {
12
- search: "import.meta.url",
13
- replace: "require('url').pathToFileURL(__filename).toString()",
14
- flags: "g"
15
- }
16
- },
17
6
  {
18
7
  test: /\.(js|jsx)$/,
19
8
  exclude: [paths.appNodeModules],
package/loaders/style.js CHANGED
@@ -13,7 +13,7 @@ const { sassOptions } = require(paths.appConfig);
13
13
 
14
14
  const _publicPath = rebuiltronConfig.buildDirs.css
15
15
  .split("/")
16
- .reduce(path => (path + "../"), "");
16
+ .reduce((path) => (path + "../"), "");
17
17
 
18
18
  const _getBaseStyleLoaders = () => ([
19
19
  ...emptyOr(isEnvDevelopment, [require.resolve("style-loader")]),
package/package.json CHANGED
@@ -1,50 +1,51 @@
1
1
  {
2
2
  "name": "rebuiltron",
3
- "version": "5.0.0",
3
+ "version": "6.0.1",
4
4
  "author": "Arkellys",
5
5
  "license": "MIT",
6
- "bin": "bin/index.js",
6
+ "bin": {
7
+ "rebuiltron": "bin/index.js"
8
+ },
7
9
  "scripts": {
8
10
  "lint": "eslint ."
9
11
  },
10
12
  "devDependencies": {
11
13
  "eslint": "^8.57.0",
12
- "eslint-config-arklint": "^1.5.0"
14
+ "eslint-config-arklint": "^1.11.0"
13
15
  },
14
16
  "dependencies": {
15
- "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
17
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
16
18
  "@svgr/webpack": "^8.1.0",
17
- "@swc/core": "^1.4.16",
18
- "browserslist": "^4.23.0",
19
+ "@swc/core": "^1.7.26",
20
+ "browserslist": "^4.23.3",
19
21
  "case-sensitive-paths-webpack-plugin": "^2.4.0",
20
22
  "copy-webpack-plugin": "^12.0.2",
21
- "css-loader": "^7.1.1",
22
- "css-minimizer-webpack-plugin": "^6.0.0",
23
- "detect-port": "^1.5.1",
23
+ "css-loader": "^7.1.2",
24
+ "css-minimizer-webpack-plugin": "^7.0.0",
25
+ "detect-port": "^1.6.1",
24
26
  "fs-extra": "^11.2.0",
25
27
  "html-webpack-plugin": "^5.6.0",
26
28
  "lodash": "^4.17.21",
27
- "mini-css-extract-plugin": "^2.9.0",
28
- "postcss": "^8.4.38",
29
+ "mini-css-extract-plugin": "^2.9.1",
30
+ "postcss": "^8.4.47",
29
31
  "postcss-flexbugs-fixes": "^5.0.2",
30
32
  "postcss-loader": "^8.1.1",
31
- "postcss-normalize": "^10.0.1",
32
- "postcss-preset-env": "^9.5.6",
33
+ "postcss-normalize": "^13.0.0",
34
+ "postcss-preset-env": "^10.0.3",
33
35
  "react-dev-utils": "^12.0.1",
34
- "react-refresh": "^0.14.0",
36
+ "react-refresh": "^0.14.2",
35
37
  "resolve-url-loader": "^5.0.0",
36
- "sass-loader": "^14.2.1",
38
+ "sass-loader": "^16.0.1",
37
39
  "source-map-loader": "^5.0.0",
38
40
  "spinnies": "^0.5.1",
39
- "string-replace-loader": "^3.1.0",
40
41
  "style-loader": "^4.0.0",
41
42
  "swc-loader": "^0.2.6",
42
43
  "terser-webpack-plugin": "^5.3.10",
43
- "webpack": "^5.91.0",
44
- "webpack-dev-server": "^5.0.4"
44
+ "webpack": "^5.94.0",
45
+ "webpack-dev-server": "^5.1.0"
45
46
  },
46
47
  "peerDependencies": {
47
- "electron": ">=28.0.0",
48
+ "electron": ">=32.0.0",
48
49
  "react": ">=18.0.0",
49
50
  "react-dom": ">=18.0.0"
50
51
  },
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
- configFileName: "rebuiltron.config.cjs",
2
+ configFileName: "rebuiltron.config.js",
3
3
  buildDirs: {
4
4
  js: "static/js",
5
5
  media: "static/media",
package/scripts/build.js CHANGED
@@ -3,14 +3,11 @@ process.env.NODE_ENV = "production";
3
3
 
4
4
 
5
5
  const { measureFileSizesBeforeBuild, printFileSizesAfterBuild } = require("react-dev-utils/FileSizeReporter");
6
- const { isEmpty } = require("lodash");
7
- const clearConsole = require("react-dev-utils/clearConsole");
8
6
 
9
- const checkSetup = require("../checkSetup");
7
+ const buildApp = require("../tasks/buildApp");
8
+ const checkSetup = require("../tasks/checkSetup");
10
9
  const paths = require("../helpers/paths");
11
10
  const { exitProcessWithError } = require("../helpers/utils");
12
- const log = require("../helpers/logger");
13
- const spinnies = require("../helpers/spinnies");
14
11
 
15
12
 
16
13
  process.on("unhandledRejection", exitProcessWithError);
@@ -20,19 +17,9 @@ const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
20
17
 
21
18
  checkSetup
22
19
  .then(() => measureFileSizesBeforeBuild(paths.appBuild))
23
- .then(previousFileSizes => {
24
- const buildApp = require("../buildApp");
25
- return buildApp(previousFileSizes);
26
- })
27
- .then(({ stats, previousFileSizes, warnings }) => {
28
- spinnies.remove("build");
29
- clearConsole();
30
-
31
- isEmpty(warnings)
32
- ? log.success("Compiled successfully!")
33
- : log.warning(["Compiled with warnings.\n", warnings.join("\n\n")]);
34
-
35
- console.log("File sizes after gzip:\n");
20
+ .then((previousFileSizes) => buildApp(previousFileSizes))
21
+ .then(({ stats, previousFileSizes }) => {
22
+ console.log("\nFile sizes after gzip:\n");
36
23
 
37
24
  printFileSizesAfterBuild(
38
25
  stats,
package/scripts/start.js CHANGED
@@ -4,21 +4,25 @@ process.env.NODE_ENV = "development";
4
4
 
5
5
  const clearConsole = require("react-dev-utils/clearConsole");
6
6
 
7
- const checkSetup = require("../checkSetup");
7
+ const checkSetup = require("../tasks/checkSetup");
8
+ const startDevServer = require("../tasks/startDevServer");
9
+ const compileMain = require("../tasks/compileMain");
10
+ const watchPreloads = require("../tasks/watchPreloads");
11
+ const startElectron = require("../tasks/startElectron");
8
12
  const { exitProcessWithError } = require("../helpers/utils");
9
- const spinnies = require("../helpers/spinnies");
10
13
 
11
14
 
12
15
  process.on("unhandledRejection", exitProcessWithError);
13
16
 
14
- checkSetup.then(([port]) => {
15
- const createDevServer = require("../createDevServer");
16
- const devServer = createDevServer(port);
17
-
17
+ checkSetup.then(async ([port]) => {
18
18
  clearConsole();
19
- spinnies.add("devServer", { text: "Starting the development server" });
20
19
 
21
- devServer.start();
20
+ await watchPreloads();
21
+ await compileMain();
22
+ const devServer = await startDevServer(port);
23
+ await startElectron(port);
24
+
25
+ console.log();
22
26
 
23
27
  ["SIGINT", "SIGTERM"].forEach((sig) => {
24
28
  process.on(sig, async () => {
@@ -0,0 +1,65 @@
1
+ const fsExtra = require("fs-extra");
2
+ const webpack = require("webpack");
3
+ const clearConsole = require("react-dev-utils/clearConsole");
4
+ const formatWebpackMessages = require("react-dev-utils/formatWebpackMessages");
5
+ const { bold, green, yellow } = require("chalk");
6
+
7
+ const paths = require("../helpers/paths");
8
+ const webpackConfig = require("../webpack.config");
9
+ const spinnies = require("../helpers/spinnies");
10
+ const log = require("../helpers/logger");
11
+
12
+
13
+ /**
14
+ * Builds the app for production.
15
+ * @param {any} previousFileSizes - Size of the files of the previous build
16
+ * @returns {Promise<{ stats: webpack.Stats | undefined, previousFileSizes: any }>} Result of the build
17
+ */
18
+
19
+ module.exports = (previousFileSizes) => {
20
+ fsExtra.emptyDirSync(paths.appBuild);
21
+ const compiler = webpack(webpackConfig.production);
22
+
23
+ return new Promise((resolve, reject) => {
24
+ clearConsole();
25
+ spinnies.add("build", { text: "Creating the production build" });
26
+
27
+ compiler.run((error, stats) => {
28
+ if (!error) {
29
+ return resolve({
30
+ stats,
31
+ previousFileSizes
32
+ });
33
+ }
34
+
35
+ spinnies.fail("build", {
36
+ text: "Failed to compile"
37
+ });
38
+
39
+ reject(error);
40
+ });
41
+
42
+ compiler.hooks.done.tap("done", (stats) => {
43
+ clearConsole();
44
+
45
+ const statsData = stats.toJson({ all: false, warnings: true, errors: true });
46
+ const { errors, warnings } = formatWebpackMessages(statsData);
47
+
48
+ if (stats.hasErrors()) throw new Error(errors);
49
+
50
+ if (stats.hasWarnings()) {
51
+ spinnies.update("build", {
52
+ text: `Compiled ${yellow(bold("with warnings"))}`,
53
+ status: "stopped",
54
+ color: "white"
55
+ });
56
+
57
+ return log.warning(warnings.join("\n\n"));
58
+ }
59
+
60
+ spinnies.succeed("build", {
61
+ text: `Compiled ${green(bold("successfully"))}`
62
+ });
63
+ });
64
+ });
65
+ };
@@ -2,11 +2,12 @@ const fs = require("fs");
2
2
 
3
3
  const detect = require("detect-port");
4
4
 
5
- const { isEnvProduction } = require("./helpers/environment");
6
- const rebuiltronConfig = require("./rebuiltronConfig");
7
- const { resolveApp } = require("./helpers/utils");
5
+ const { isEnvProduction } = require("../helpers/environment");
6
+ const rebuiltronConfig = require("../rebuiltronConfig");
7
+ const { resolveApp } = require("../helpers/utils");
8
8
 
9
9
  /**
10
+ * @type {Promise<number>}
10
11
  * Checks whether the default port is available, otherwise resolves with the next available port.
11
12
  */
12
13
 
@@ -20,6 +21,7 @@ const checkPort = new Promise(async (resolve) => {
20
21
  });
21
22
 
22
23
  /**
24
+ * @type {Promise<void>}
23
25
  * Checks whether Electron's entry point is set.
24
26
  */
25
27
 
@@ -38,6 +40,7 @@ const checkEntryPoint = new Promise((resolve, reject) => {
38
40
  });
39
41
 
40
42
  /**
43
+ * @type {Promise<void>}
41
44
  * Checks whether Rebuiltron's config file exists and contains the required fields.
42
45
  */
43
46
 
@@ -51,8 +54,9 @@ const checkAppConfig = new Promise((resolve, reject) => {
51
54
  ]);
52
55
  }
53
56
 
54
- const { preloads, renderers } = require(configPath);
57
+ const { main, preloads, renderers } = require(configPath);
55
58
 
59
+ if (!main) reject("Configuration error: `main` field is required.");
56
60
  if (!preloads) reject("Configuration error: `preloads` field is required.");
57
61
  if (!renderers) reject("Configuration error: `renderers` field is required.");
58
62
 
@@ -0,0 +1,41 @@
1
+ const webpack = require("webpack");
2
+ const { bold, green } = require("chalk");
3
+ const formatWebpackMessages = require("react-dev-utils/formatWebpackMessages");
4
+
5
+ const webpackConfig = require("../webpack.config");
6
+ const spinnies = require("../helpers/spinnies");
7
+
8
+
9
+ /**
10
+ * Compiles main entry file for development.
11
+ * @returns {Promise<void>} Promise resolving when compilation is successful
12
+ */
13
+
14
+ module.exports = () => {
15
+ const mainCompiler = webpack(webpackConfig.development.main);
16
+
17
+ return new Promise((resolve, reject) => {
18
+ spinnies.add("compile-main", { text: "Compiling main file..." });
19
+
20
+ mainCompiler.run((error) => {
21
+ if (!error) return resolve();
22
+
23
+ spinnies.fail("compile-main", {
24
+ text: "Failed to compile main file"
25
+ });
26
+
27
+ reject(error);
28
+ });
29
+
30
+ mainCompiler.hooks.done.tap("done", (stats) => {
31
+ const statsData = stats.toJson({ all: false, warnings: true, errors: true });
32
+ const { errors } = formatWebpackMessages(statsData);
33
+
34
+ if (stats.hasErrors()) throw new Error(errors);
35
+
36
+ spinnies.succeed("compile-main", {
37
+ text: `Main file compiled ${green(bold("successfully"))}`
38
+ });
39
+ });
40
+ });
41
+ };
@@ -0,0 +1,45 @@
1
+ const webpack = require("webpack");
2
+ const WebpackDevServer = require("webpack-dev-server");
3
+ const clearConsole = require("react-dev-utils/clearConsole");
4
+ const { bold } = require("chalk");
5
+
6
+ const webpackConfig = require("../webpack.config");
7
+ const devServerConfig = require("../webpackDevServer.config");
8
+ const spinnies = require("../helpers/spinnies");
9
+
10
+ /**
11
+ * Starts the development server.
12
+ * @param {number} port - Port on which the renderers are served
13
+ * @returns {Promise<WebpackDevServer>} Development server instance
14
+ */
15
+
16
+ module.exports = (port) => (
17
+ new Promise(async (resolve) => {
18
+ let devServer;
19
+ let isFirstRun = true;
20
+
21
+ const rendererCompiler = webpack(webpackConfig.development.renderers);
22
+
23
+ spinnies.add("devServer", { text: "Starting the development server" });
24
+
25
+ rendererCompiler.hooks.invalid.tap("invalid", () => {
26
+ clearConsole();
27
+ spinnies.add("compile", { text: "Compiling..." });
28
+ });
29
+
30
+ rendererCompiler.hooks.done.tap("done", () => {
31
+ if (isFirstRun) {
32
+ isFirstRun = false;
33
+
34
+ spinnies.succeed("devServer", { text: `Development server running on port ${bold(port)}` });
35
+ return resolve(devServer);
36
+ }
37
+
38
+ clearConsole();
39
+ spinnies.remove("compile");
40
+ });
41
+
42
+ devServer = new WebpackDevServer({ ...devServerConfig, port }, rendererCompiler);
43
+ await devServer.start();
44
+ })
45
+ );
@@ -0,0 +1,40 @@
1
+ const { spawn } = require("child_process");
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+
5
+ const spinnies = require("../helpers/spinnies");
6
+
7
+
8
+ /**
9
+ * Starts Electron.
10
+ * @param {number} port - Port on which the renderer is served
11
+ */
12
+
13
+ module.exports = (port) => (
14
+ new Promise((resolve, reject) => {
15
+ spinnies.add("electron", { text: "Starting Electron" });
16
+
17
+ try {
18
+ const electronPath = require.resolve("electron");
19
+ const electronModulePath = path.dirname(electronPath);
20
+ const pathFile = path.join(electronModulePath, "path.txt");
21
+ const executablePath = fs.readFileSync(pathFile, "utf-8");
22
+ const electronExtPath = path.join(electronModulePath, "dist", executablePath);
23
+
24
+ spawn(electronExtPath, ["."], {
25
+ stdio: "inherit",
26
+ env: {
27
+ ...process.env,
28
+ DEV_LOCAL_URL: `http://localhost:${port}`
29
+ }
30
+ }).on("close", process.exit);
31
+
32
+ spinnies.succeed("electron", { text: "Electron started" });
33
+ resolve();
34
+
35
+ } catch (error) {
36
+ spinnies.fail("electron", { text: "Failed to start Electron" });
37
+ reject(error);
38
+ }
39
+ })
40
+ );
@@ -0,0 +1,73 @@
1
+ const webpack = require("webpack");
2
+ const { red, bold, green, yellow } = require("chalk");
3
+ const formatWebpackMessages = require("react-dev-utils/formatWebpackMessages");
4
+ const clearConsole = require("react-dev-utils/clearConsole");
5
+
6
+ const webpackConfig = require("../webpack.config");
7
+ const spinnies = require("../helpers/spinnies");
8
+ const log = require("../helpers/logger");
9
+
10
+
11
+ /**
12
+ * Sets up the watcher of preload files.
13
+ * @returns {Promise<void>} Promise resolving when compilation is successful
14
+ */
15
+
16
+ module.exports = () => {
17
+ const preloadCompiler = webpack(webpackConfig.development.preloads);
18
+
19
+ return new Promise((resolve, reject) => {
20
+ let isFirstWatch = true;
21
+
22
+ preloadCompiler.watch({}, (error) => {
23
+ if (!isFirstWatch) return;
24
+ isFirstWatch = false;
25
+
26
+ if (!error) return resolve();
27
+
28
+ spinnies.fail("watch", {
29
+ text: "Failed to compile preload file(s)"
30
+ });
31
+
32
+ reject(error);
33
+ });
34
+
35
+ preloadCompiler.hooks.compile.tap("compile", () => {
36
+ if (!isFirstWatch) clearConsole();
37
+
38
+ spinnies.add("watch", {
39
+ text: "Compiling preload file(s)..."
40
+ });
41
+ });
42
+
43
+ preloadCompiler.hooks.done.tap("done", (stats) => {
44
+ const statsData = stats.toJson({ all: false, warnings: true, errors: true });
45
+ const { errors, warnings } = formatWebpackMessages(statsData);
46
+
47
+ if (stats.hasErrors()) {
48
+ if (isFirstWatch) throw new Error(errors); // Stops the process
49
+
50
+ spinnies.fail("watch", {
51
+ text: `Preload file(s) compiled ${red(bold("with errors"))}`,
52
+ failColor: "white"
53
+ });
54
+
55
+ return log.error(errors.join("\n\n"));
56
+ }
57
+
58
+ if (stats.hasWarnings()) {
59
+ spinnies.update("watch", {
60
+ text: `Preload file(s) compiled ${yellow(bold("with warnings"))}`,
61
+ status: "stopped",
62
+ color: "white"
63
+ });
64
+
65
+ return log.warning(warnings.join("\n\n"));
66
+ }
67
+
68
+ spinnies.succeed("watch", {
69
+ text: `Preload file(s) compiled ${green(bold("successfully"))}`
70
+ });
71
+ });
72
+ });
73
+ };
package/webpack.config.js CHANGED
@@ -1,7 +1,13 @@
1
- const { isEnvDevelopment } = require("./helpers/environment");
2
1
  const rendererConfig = require("./configurations/renderers");
3
2
  const preloadConfig = require("./configurations/preloads");
4
3
  const mainConfig = require("./configurations/main");
5
4
 
6
5
 
7
- module.exports = isEnvDevelopment ? rendererConfig : [rendererConfig, mainConfig, preloadConfig];
6
+ module.exports = {
7
+ development: {
8
+ renderers: rendererConfig,
9
+ main: mainConfig,
10
+ preloads: preloadConfig
11
+ },
12
+ production: [rendererConfig, mainConfig, preloadConfig]
13
+ };
package/buildApp.js DELETED
@@ -1,33 +0,0 @@
1
- const fsExtra = require("fs-extra");
2
- const clearConsole = require("react-dev-utils/clearConsole");
3
- const webpack = require("webpack");
4
- const formatWebpackMessages = require("react-dev-utils/formatWebpackMessages");
5
- const { isEmpty, head } = require("lodash");
6
-
7
- const paths = require("./helpers/paths");
8
- const webpackConfig = require("./webpack.config");
9
- const spinnies = require("./helpers/spinnies");
10
-
11
-
12
- module.exports = (previousFileSizes) => {
13
- fsExtra.emptyDirSync(paths.appBuild);
14
-
15
- clearConsole();
16
- spinnies.add("build", { text: "Creating the production build" });
17
-
18
- const compiler = webpack(webpackConfig);
19
-
20
- return new Promise((resolve, reject) => {
21
- compiler.run((error, stats) => {
22
- const statsData = error
23
- ? { errors: [error?.message || error], warnings: [] }
24
- : stats.toJson({ all: false, warnings: true, errors: true });
25
-
26
- const { errors, warnings } = formatWebpackMessages(statsData);
27
-
28
- return !isEmpty(errors)
29
- ? reject(["Failed to compile.\n", head(errors)])
30
- : resolve({ stats, previousFileSizes, warnings });
31
- });
32
- });
33
- };
@@ -1,73 +0,0 @@
1
- const { spawn } = require("child_process");
2
- const path = require("path");
3
- const fs = require("fs");
4
-
5
- const webpack = require("webpack");
6
- const WebpackDevServer = require("webpack-dev-server");
7
- const clearConsole = require("react-dev-utils/clearConsole");
8
- const { bold } = require("chalk");
9
-
10
- const webpackConfig = require("./webpack.config");
11
- const devServerConfig = require("./webpackDevServer.config");
12
- const { exitProcessWithError } = require("./helpers/utils");
13
- const spinnies = require("./helpers/spinnies");
14
-
15
-
16
- let isElectronStarted = false;
17
-
18
-
19
- const _startElectron = (port) => {
20
- spinnies.add("electron", { text: "Starting Electron" });
21
-
22
- try {
23
- const electronPath = require.resolve("electron");
24
- const electronModulePath = path.dirname(electronPath);
25
- const pathFile = path.join(electronModulePath, "path.txt");
26
- const executablePath = fs.readFileSync(pathFile, "utf-8");
27
- const electronExtPath = path.join(electronModulePath, "dist", executablePath);
28
-
29
- spawn(electronExtPath, ["."], {
30
- stdio: "inherit",
31
- env: {
32
- ...process.env,
33
- DEV_LOCAL_URL: `http://localhost:${port}`
34
- }
35
- }).on("close", process.exit);
36
-
37
- spinnies.succeed("devServer", { text: `Development server running on port ${bold(port)}` });
38
- spinnies.succeed("electron", { text: "Electron started\n" });
39
-
40
- isElectronStarted = true;
41
-
42
- } catch (error) {
43
- exitProcessWithError({
44
- message: [
45
- "An error occurred while starting Electron.",
46
- "Please make sure the dependency `electron` is installed."
47
- ],
48
- stack: error.stack || error
49
- });
50
- }
51
- };
52
-
53
- module.exports = (port) => {
54
- const compiler = webpack(webpackConfig);
55
-
56
- // Starting (re)compiling
57
-
58
- compiler.hooks.invalid.tap("invalid", () => {
59
- clearConsole();
60
- spinnies.add("compile", { text: "Compiling..." });
61
- });
62
-
63
- // Finished (re)compiling
64
-
65
- compiler.hooks.done.tap("done", () => {
66
- clearConsole();
67
- spinnies.remove("compile");
68
-
69
- if (!isElectronStarted) _startElectron(port);
70
- });
71
-
72
- return new WebpackDevServer({ ...devServerConfig, port }, compiler);
73
- };