Electron-builder: Production application is blank

Created on 8 Oct 2017  路  2Comments  路  Source: electron-userland/electron-builder


  • Version: 19.34.2

  • Target: macOS, DMG
  • Node: v8.4.0
  • NPM: 5.3.0
  • Yarn: 0.27.5

This is my first ever Electron application, so I might have overseen something entirely obvious.

I have made a React application, using Babel and bundled with Webpack3. I am trying to use electron-builder for creating my production application. I am first and foremost completely new to Electron, so I am even unsure on how to debug the running production application.

The issue is, that once I have installed the application, the main window is just the background color of the window - no application is embedded, I can only see the window - no alerts.

My folder structure:

src/
  - /components
  - ...etc
  - index.js
main.js
index.html
webpack.config.prod.js
package.json
...etc

My build steps is:

yarn build which bundles files into ./dist/:
screen shot 2017-10-08 at 08 05 56

yarn pack whichs runs electron-builder and builds the files into ./release/

My configurations is as follows:

{
    "name": "my-application-test",
    "version": "0.0.1",
    "author": "Jan Hartmann",
    "description": "My Application Test",
    "private": true,
    "main": "main.js",
    "scripts": {
        "build":
            "cross-env NODE_ENV=production webpack --config webpack.config.prod.js",
        "start":
            "concurrently \"cross-env NODE_ENV=development webpack-dev-server --config webpack.config.dev.js\" \"cross-env NODE_ENV=development electron -r babel-register ./main.js\"",
        "pack": "electron-builder --dir",
        "dist": "electron-builder",
        "postinstall": "electron-builder install-app-deps"
    },
    "build": {
        "productName": "MyApplication",
        "appId": "com.demo.MyApplicationTest",
        "directories": {
            "buildResources": "resources",
            "output": "release"
        }
    },
    "devDependencies": {
        "babel-core": "^6.26.0",
        "babel-eslint": "^8.0.1",
        "babel-loader": "^7.1.2",
        "babel-plugin-transform-class-properties": "^6.24.1",
        "babel-plugin-transform-object-rest-spread": "^6.26.0",
        "babel-polyfill": "^6.26.0",
        "babel-preset-env": "^1.6.0",
        "babel-preset-react": "^6.24.1",
        "babel-register": "^6.26.0",
        "concurrently": "^3.5.0",
        "cross-env": "^5.0.5",
        "css-loader": "^0.28.7",
        "electron": "^1.7.8",
        "electron-builder": "^19.34.2",
        "electron-devtools-installer": "^2.2.0",
        "eslint": "^4.8.0",
        "eslint-plugin-react": "^7.4.0",
        "extract-text-webpack-plugin": "^3.0.1",
        "file-loader": "^1.1.5",
        "html-webpack-plugin": "^2.30.1",
        "node-sass": "^4.5.3",
        "style-loader": "^0.19.0",
        "url-loader": "^0.6.2",
        "webpack": "^3.6.0",
        "webpack-dev-server": "^2.9.1"
    },
    "dependencies": {
        "babel-runtime": "^6.26.0",
        "classnames": "^2.2.5",
        "color": "^2.0.0",
        "deepmerge": "^1.5.2",
        "global": "^4.3.2",
        "graphql-tag": "^2.4.2",
        "history": "^4.7.2",
        "jss": "^9.0.0",
        "jss-preset-default": "^4.0.1",
        "leaflet": "^1.2.0",
        "moment": "^2.18.1",
        "prop-types": "^15.6.0",
        "react": "^16.0.0",
        "react-apollo": "^1.4.16",
        "react-dom": "^16.0.0",
        "react-hot-loader": "^3.0.0-beta.7",
        "react-jss": "^7.2.0",
        "react-leaflet": "^1.7.0",
        "react-redux": "^5.0.6",
        "react-router-dom": "^4.2.2",
        "react-router-redux": "next",
        "react-tabs": "^2.1.0",
        "react-world-flags": "^0.0.3",
        "redux": "^3.7.2",
        "redux-actions": "^2.2.1",
        "redux-saga": "^0.15.6",
        "sass-loader": "^6.0.6"
    }
}

This is the webpack.config.prod.js file:

const webpack = require("webpack");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    devtool: "cheap-module-source-map",
    context: path.join(__dirname, "./src"),
    target: "electron-renderer",
    entry: {
        app: ["babel-polyfill", "./styles/index.scss", "./index"]
    },
    output: {
        path: path.resolve(__dirname, "./dist"),
        publicPath: "/",
        filename: "[name].bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: ExtractTextPlugin.extract({
                    fallback: "style-loader",
                    use: ["css-loader", "sass-loader"]
                })
            },
            {
                test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
                use: "file-loader?name=assets/[name].[ext]"
            },
            {
                test: /\.(png|jpg|gif)$/,
                use: "url-loader?limit=8192&name=assets/[name].[ext]"
            },
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: ["babel-loader?cacheDirectory"]
            },
            {
                test: /\.(graphql|gql)$/,
                loader: "graphql-tag/loader"
            }
        ]
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: "vendor",
            filename: "[name].bundle.js",
            minChunks: Infinity
        }),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false,
                screw_ie8: true,
                conditionals: true,
                unused: true,
                comparisons: true,
                sequences: true,
                dead_code: true,
                evaluate: true,
                if_return: true,
                join_vars: true
            },
            output: {
                comments: false
            }
        }),
        new webpack.HashedModuleIdsPlugin(),
        new webpack.optimize.ModuleConcatenationPlugin(),
        new webpack.NamedModulesPlugin(),
        new webpack.NoEmitOnErrorsPlugin(),
        new ExtractTextPlugin("[name].css"),
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "index.html")
        }),
        new webpack.DefinePlugin({
            "process.env.NODE_ENV": JSON.stringify("production")
        })
    ],
    resolve: {
        alias: {
            actions: path.resolve(__dirname, "src/actions/"),
            assets: path.resolve(__dirname, "src/assets/"),
            components: path.resolve(__dirname, "src/components/"),
            containers: path.resolve(__dirname, "src/containers/"),
            theme: path.resolve(__dirname, "src/theme/"),
            data: path.resolve(__dirname, "src/data/")
        }
    }
};

This is the main.js file in the root of my directory (outside ./src/):

const { app, BrowserWindow } = require("electron");
const path = require("path");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", () => {
    if (process.env.NODE_ENV === "development") {
        installExtensions();
    }

    createWindow();
});

// Quit when all windows are closed.
app.on("window-all-closed", () => {
    // On macOS it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== "darwin") {
        app.quit();
    }
});

app.on("activate", () => {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (mainWindow === null) {
        createWindow();
    }
});

const createWindow = () => {
    // Create the browser window.
    mainWindow = new BrowserWindow({
        backgroundColor: "#191919",
        width: 1600,
        minWidth: 1600,
        height: 900,
        titleBarStyle: "hiddenInset",
        icon: path.join(__dirname, "resources/icons/64x64.png")
    });

    // and load the index.html of the app.
    if (process.env.NODE_ENV === "development") {
        mainWindow.loadURL("http://localhost:3001");
    } else {
        mainWindow.loadURL(`file://${__dirname}/dist/index.html`);
    }

    mainWindow.once("ready-to-show", () => {
        mainWindow.webContents.openDevTools();
    });

    // Emitted when the window is closed.
    mainWindow.on("closed", () => {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        mainWindow = null;
    });
};

const installExtensions = () => {
    const installer = require("electron-devtools-installer");
    const extensions = ["REACT_DEVELOPER_TOOLS", "REDUX_DEVTOOLS"];

    extensions.map(name =>
        installer
            .default(installer[name])
            .then(name => console.log(`Added Extension:  ${name}`))
            .catch(err => console.log("An error occurred: ", err))
    );
};

I am unsure why this is not working, and I hope the community have some great advices to get me running! Let me know if anything is needed more than this.

Kind regards,
Jan

question

Most helpful comment

Hi. It seems the same (or similar) issue was raised here - https://github.com/react-boilerplate/react-boilerplate/issues/488

I assumed by production you mean your Electron app main process window URL is set to a path to a local HTML file using the file:// protocol. If that is the case, Electron app will not work with React Router using browserHistory. I think you can try changing React Router from using browserHistory to hashHistory, and then it will work fine.

  • browserHistory only works with HTTP protocol, e.g.:
    electron.BrowserWindow.loadURL('http://localhost:3000')
  • hashHistory works with local file and HTTP protocol, e.g:
    electron.BrowserWindow.loadURL('file:///Users/foobar/dev/electron-app/index.html')

More info about the history JS library is here.

I hope that helps.

All 2 comments

Hi. It seems the same (or similar) issue was raised here - https://github.com/react-boilerplate/react-boilerplate/issues/488

I assumed by production you mean your Electron app main process window URL is set to a path to a local HTML file using the file:// protocol. If that is the case, Electron app will not work with React Router using browserHistory. I think you can try changing React Router from using browserHistory to hashHistory, and then it will work fine.

  • browserHistory only works with HTTP protocol, e.g.:
    electron.BrowserWindow.loadURL('http://localhost:3000')
  • hashHistory works with local file and HTTP protocol, e.g:
    electron.BrowserWindow.loadURL('file:///Users/foobar/dev/electron-app/index.html')

More info about the history JS library is here.

I hope that helps.

@janhartmann, did you ever figure this one out? I have a very similar setup with the same results <- also my first Electron build.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NPellet picture NPellet  路  3Comments

alexstrat picture alexstrat  路  3Comments

iklemm picture iklemm  路  3Comments

jhg picture jhg  路  3Comments

philcockfield picture philcockfield  路  3Comments