FrakenApp #1: Part 6, Webpack!

For the last couple of weeks, I tried to get React to work with Phone Home App, and I would come face to face with Webpack! Getting Webpack and React to work in the browser isn't such a big deal, but add Electron to the mix... So I decided to get Webpack to work with Electron and do React later.

Webpack and Electron

The problem I had with Webpack is that, by default, it wants to stuff everything into a single bundle file. Electron needs at least three files: the main process's JavaScript file, the renderer's JavaScript file, and an HTML file.

2 exports

I figured out how split the export into two parts. Each one would have a different target telling Webpack what I was trying to build, 'electron-main' and 'electron-renderer'.

HtmlWebpackPlugin

I renamed index.html to index.ejs, took out the script tag pointing to renderer.js, and let the HtmlWebpackPlugin wepback plugin create the HTML file.

webpack.config.js

Here is my webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');

const commonConfig = {
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [
      {
        test: /\.tsx$/,
        loader: 'ts-loader'
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.ts', '.json']
  }
};

module.exports = [
  // 1. The Main bundle
  Object.assign(
  {
    target: 'electron-main',
    entry: {main: './main.ts'}
  },
  commonConfig),
  // 2. The Renderer bundle
  Object.assign(
  {
    target: 'electron-renderer',
    entry: {renderer: './renderer.ts'},
    plugins: [
      // 3. The HTML file 
      // (I converted the old html file to ejs 
      // so Webpack would build here)
      new HtmlWebpackPlugin(
      { 
        filename: `index.html`, 
        template: './index.ejs'         
      })
    ]
  },
  commonConfig)
];

index.ejs

index.ejs is the HTML of the old index.html with all the script tags removed. Webpack will add the link to the renderer script.

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Phone Home App</title>
</head>

<body>
    <h1>Phone Home App</h1>
    <br />
    <input type="text" id="messageText" name="messageText" value="" />
    <input type="button" id="phoneHome" value="Phone Home" />
    <br />
    <h2>Incoming Calls</h2>
    <div id="received-data-content"></div>
</body>

</html>

More Dev Dependencies

In the process, I added a whole mess of Dev Dependencies. Since Webpack adds any dependency to the bundle, Dev Dependencies are best. For example, jQuery will be added to renderer.js, so it doesn't need to be in node-modules.

You can add each like this in npm:

npm install some-package --save-dev

or in yarn:

yarn add some-package -D

Here are all the dependencies:

  • @types/electron
  • @types/jquery
  • @types/node
  • babel-core
  • babel-loader
  • babel-preset-es2015-node
  • babel-preset-react
  • cross-env
  • electron
  • electron-is-dev
  • html-webpack-plugin
  • jquery
  • net
  • node-loader
  • standard
  • standard-loader
  • ts-loader
  • ts-node
  • typescript
  • webpack
  • webpack-cli

Launching

Before launching the client, you will need to launch a server, either simple-tcp-repeater or simple-tcp-repeater-net before you launch the client.

npm or yarn

I edited the scripts section of package.json to build with Webpack.

  "scripts": {
    "build": "webpack --config webpack.config.js --mode=development",
    "client": "electron ./dist/main.js",
    "start": "npm run build && npm run client"
  },

build builds without launching, client launches without building, and start does both.

You can launch the app with npm like this:

npm run start

or with yarn:

yarn start

vscode

I updated VSCode's launch.json and added a tasks.json to

.vscode/launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: 
    // https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
          "name": "Client: Debug Main Process",
          "type": "node",
          "request": "launch",
          "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
          "runtimeArgs": [
            "--remote-debugging-port=9222"
          ],
          "cwd": "${workspaceRoot}",
          "program": "${workspaceRoot}/dist/main.js",
          "timeout": 20000,
          "sourceMaps": true,
          "preLaunchTask": "build"    
        },        
        {
          "name": "Client: Attach to Render Process",
          "type": "chrome",
          "request": "attach",
          "port": 9222,
          "webRoot": "${workspaceRoot}/dist/index.html"
        }
      ],
    "compounds": [
      {
        "name": "Client",
        "configurations": [
          "Client: Debug Main Process", 
          "Client: Attach to Render Process"
        ]
      }
    ]
}
.vscode/tasks.json

In launch.json, there is the line "preLaunchTask": "build", this refers to a new file, tasks.json, which essentially points to build from package.json. This task is executing npm run build.

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "command": "npm",
  "tasks": [
    {
      "label": "build",
      "type": "shell",
      "args": [
        "run",
        "build"
      ],
      "problemMatcher": []
    }
  ]
}

Failed Experiments

I have to confess; this is a result of several failed experiments. Since this blog is more of a record of my explorations than a purely technical blog, I should summarize my more extensive experiments.

Directly To React.

I tried to graph some React directly into Phone Home App. Since many React samples use Webpack, I ended up with a hybrid app with only the React part bundled. This approach worked OK as long as I didn't have to communicate between the bundled and unbundled components.

Add Webpack, then React

Then I tried to add Webpack to Phone Home App. First, I tried to reverse engineer Electron React Boilerplate, but that was too far from the current state of the Phone Home App and I wanted to see more of the guts. I did a similar experiment with Electron Forge. These projects taught me quite a bit and showed me that I needed to read more about Webpack.

Forget React, just Webpack

I gave up trying to get Webpack and React working in one step. I was getting resolve 'fs' errors. I solved this by setting the target to 'electron-renderer'.

At this point, I watched the PluralSite course Webpack Fundamentals like a TV show and then started to dig in deeper into Webpack.

After finishing the Webpack Fundamentals course, I searched for electron-renderer and Wepback. I ran across Scott Logic's Setting up a TypeScript Electron app in WebPack, which I used as a basis of my transformations.

Finish Line

After this long tech journey, I conquered! I am starting to understand how these parts fit together.

Progress in the FrakenApp #1

What I have

  • Still using .NET Core, though I didn't touch it this time.
  • The app now uses TypeScript.
  • Now using Webpack (new requirement)

What's still missing

  • No Oracle.
  • Not using strict TypeScript.
  • Using JQuery, not React.js.
  • Not building the Electron App; I'm running it from VS Code.
  • Didn't test it on the Mac this cycle

The Code

The code is available on my GitHub as Frakenapp-01 or the Release for this article