electron-react-boilerplate / electron-react-boilerplate

A Foundation for Scalable Cross-Platform Apps
https://electron-react-boilerplate.js.org/
MIT License
23.16k stars 3.85k forks source link

Publishing app to web & electron – preferred approach? #80

Open t1mmen opened 8 years ago

t1mmen commented 8 years ago

I'm looking to publish my app both to the web, and as a standalone executable.

Is anyone doing this, and if so, care to share your preferred approach? If not, is this something the maintainers are interested in as part of the repo? I'll try to lend a helping hand in the coming weeks if so.

chentsulin commented 8 years ago

I think we should not include web stuff into this boilerplate, it will increase unnecessary complexity in a desktop-only application. But if someone is interested in how to build them together, I can work on a separate repo to demo it in next couple of weeks.

t1mmen commented 8 years ago

@chentsulin Makes sense to keep it separate for complexities sake. I'd be very interested in a demo of how to, though!

alexhawkins commented 8 years ago

I'd be very interested in this as well. @chentsulin Let me know if you decide to do this. I'm going to see if I can get it to work on my own but any help from anyone would be much appreciated. Shoot me an email @ alex.hawkins@shiphawk.com

borisadimov commented 8 years ago

Yo @chentsulin, I'm currently working with your boilerplate, it awesome, thank you. I'll need website build feature in next two weeks, so if I can help you somehow with it, please tell me.

acao commented 8 years ago

I'm doing this with a modified version of this boilerplate.

All i had to do was handle argv from make-webpack-config, and when 'web' is provided then the webpack options.target = web, otherwise the electron renderer as this boilerplate provides.

Deployment is another matter - I just adapted the package.json scripts to a makefile and deploy the web version to s3 (after running a build-web using --target web as described above).

It's actually not very complicated at all. The only thing I have left to is find some way to allow the renderer process to detect whether its being run in electron or in the web, so that for example 'openDialog' function can choose to open a native dialog or material ui dialog.

The one thing that will get you caught is what build you're running in development depending on whether you are developing for electron or web, and same for deployment. Oh and make sure hot-dev-server knows what it needs to do as well.

acao commented 8 years ago

Apologies, I forked this project a few months ago and the webpack config has changed significantly since then. I hope that this still helps folks who are looking to get this working!

borisadimov commented 8 years ago

https://gist.github.com/flywithmemsl/0d9eba3c7e0ebc3dddc3 — works for me replaced commonjs2 with var in libraryTarget removed webpackTargetElectronRenderer

acao commented 8 years ago

Cool! Also forgot to mention, i just passed --target web to wepback-dev-server/wepback, no argv handling

From my makefile:

build-web:
    @node_modules/.bin/webpack --config webpack/webpack.config.production.js --progress --profile --colors --target web

build:
    @node_modules/.bin/webpack --config webpack/webpack.config.production.js --progress --profile --colors

hot-dev-web:
    @node_modules/.bin/webpack-dev-server --config webpack/webpack-hot-dev-server.config.js --hot --progress --colors --port 2992 --inline --target web

if you specify --target to webpack-dev-server or webpack, it overrides the webpack options.target prop.

This assumes you have the webpackTargetElectronRenderer set as the options.target in your webpack config.

t1mmen commented 8 years ago

I can confirm that @acao's approach works fine. Thanks!

t1mmen commented 8 years ago

Another thing to keep in mind: Polyfills weren't set up by default when I cloned this project, so you might want to include that when building for the web (assuming you're using ES6/7 features)

In the webpack config, change:

config.entry = './app/index'

... to...

config.entry = ['babel-core/polyfill', './app/index'];

marekhrabe commented 8 years ago

Thanks a lot for your research, guys. I will try to add web support to my project this week.

I'm just wondering if the solution is to create webpack target and introduce a way to detect the environment (browser/electron) it might be worth adding right to the core or at least transform this discussion into a simple docs page? I can probably help with that if @chentsulin would be interested :)

kornfleks commented 8 years ago

Hello :), The compilation work well, but I don't know how to use the files int dist/. How do I connect to the web app with my browser ? with localhost:2992 it's bring me to a file explorer of my project :/

I'm waiting for the update, thanks a lot for your work :)

cryptiklemur commented 7 years ago

Were you able to make that separate repo @chentsulin? Instructions seem unclear now. Seems like the structure has changed considerably

amilajack commented 7 years ago

there hasnt been any progress on this yet

doulmi commented 7 years ago

0 o, No one can give a simple solution?

amilajack commented 7 years ago

I haven't looked into it too much yet.

chentsulin commented 7 years ago

@doulmi I'd recommend for using nativefier to create a new web app runs on both env. It's the simplest way that I could ever find.

cryptiklemur commented 7 years ago

I'm sorry, but that is a terrible solution.

vjtc0n commented 7 years ago

Have anyone done this task successfully yet ?

amilajack commented 7 years ago

@vjtc0n not yet. I'm thinking of integrating react-boilerplate into this repo just to see how portability of web -> electron is

vjtc0n commented 7 years ago

@amilajack Thank you.

Hoping your work going well soon.

marekhrabe commented 7 years ago

I got it working in my app last summer, by making a custom webpack config for a web build. My specific solution, provided as-is (you definitely need to alter this for yourself): https://gist.github.com/marekhrabe/4a412d232d6c9f2a25d594d2a83e6ba1

Tips for integrating to your app:

I'm not sure there is a universal solution that can be added to a boilerplate, it really depends on your app.

lion19 commented 7 years ago

Has anyone got this working with the current version of the boilerplate? I attempted both @acao's solution of overriding the target to web and @marekhrabe's solution of a custom web config that does the same thing in a config file. I additionally modified the template HTML to directly import the style.css and bundle.js directly, without considering process.ENV.

Both approaches result in:

es6.number.constructor.js:20 Uncaught ReferenceError: module is not defined

The project/tooling has otherwise thus far been a dream to work with!

amilajack commented 7 years ago

@lion19 At the moment, I don't think there's a proven or recommended way of doing this.

lion19 commented 7 years ago

@amilajack Got it, thanks for the response. It does make sense to keep the web tooling separate. I'll be sure to post here if I come up with something helpful!

amilajack commented 7 years ago

It does make sense to keep the web tooling separate.

I actually think that there should be a way to do this. Its just that no one has really come up with a proven way of doing this. Also the tooling for electron and the browser are almost identical. The only difference is the target in the webpack config. Let me know if you find anything

lion19 commented 7 years ago

@amilajack I eventually did a web build working. I built a web config importing the production config. Relative to the production config I overrode the 'target' of to 'web', and additionally overrode the output.libraryTarget to 'umd' (the base config sets this to 'commonjs2'), which, if I'm not mistaken, took care of the definition of 'module' in the global scope. Additionally, I overrode the config for the HtmlWebpackPlugin to use a a separate template with web-ready references to the bundle and stylesheet.

Nantris commented 7 years ago

@lion19 Would it be possible to share your webpack configs? I'd love to help find better solutions to this for myself and other users of the boilerplate.

lion19 commented 7 years ago

Sure @Slapbox, here's the the web config. I haven't factored or anything, it could very likely just merge the couple properties that differ onto the production config if that makes sense for you. My custom webapp.html additionally needs to remove references to the process environment. Let me know how it goes!

Nantris commented 7 years ago

@lion19 thanks so much for sharing that! I won't get to that task for a couple weeks, but I'm excited to try and really thankful to have something to help guide me.

Will definitely update when I can.

Nantris commented 7 years ago

Just wanted to mention there is at least one other boilerplate out there that is attacking this issue.

Forgot to include the link....

https://github.com/timberio/molecule

Nantris commented 6 years ago

@lion19 do you have any updated version of this config? I know a good deal has changed in the past year and I'm hoping to take a crack at this later this month. If you've got any starting point for me that's further along, I'd love to use it to try and find a config general enough to warrant a pull request.

stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

germain-italic commented 6 years ago

@Slapbox Have your worked on this recently?

Nantris commented 6 years ago

I did eventually reach a working solution. Unfortunately I can't share it though as my project hardly resembles the boilerplate in any way anymore, and frankly, I'm not even sure the full extent of the changes it required anymore. There were changes to a number of config files.

Here's some things I know you need to do:

First: Make a new webpack.config file for web and associated commands in the package.json file.

Second: Ensure you're not loading any dependencies which won't work on the web, such as node's fs or any native dependencies.

Here's some bits you'll need in your new webpack.config for web (Webpack 4). Obviously some parts of them can change, but things like libraryTarget and target are essential:

 target: 'web',
 output: {
    path: path.join(__dirname, '../app/webdist'),
    publicPath: './webdist/',
    filename: 'web.prod.js',
    libraryTarget: 'umd',
  },

Sorry I don't have a ready-made solution to share. I'm happy to answer any questions you have as you try to make this work for your project though.

germain-italic commented 6 years ago

Thanks @Slapbox for pointing libraryTarget: 'umd', as I was stuck with Uncaught ReferenceError: module is not defined error and did not understand from the docs what this attribute was made for.

My app is now working with Webpack's devServer in dev mode, and served by Apache when build.

Here's a comprehensive tutorial in case someone else is stuck:

dev

package.json:

    "dev-web": "cross-env START_HOT=1 node -r babel-register ./internals/scripts/CheckPortInUse.js && cross-env START_HOT=1 yarn start-web-dev",
    "start-web-dev": "cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack-dev-server/bin/webpack-dev-server --config webpack.config.web.dev.js",

webpack.config.web.dev.js:

build

package.json:

    "build-web": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.web.prod.js --colors",

webpack.config.web.prod.js:

Nantris commented 6 years ago

Nice work!

acao commented 6 years ago

@Germain-Italic this would be a great thing to add to the wiki, maybe? then this issue can have some resolution after all these years!

germain-italic commented 6 years ago

@acao I'd gladly do it but there is a new problem I can't figure out. Thus it may not be related to the boilerplate itself, because it comes from HtmlWebpackPlugin versus Hotreload.

It starts normally:

PS C:\Users\Germain\Sites\presentations> yarn dev-web
yarn run v1.9.4
$ cross-env START_HOT=1 node -r babel-register ./internals/scripts/CheckPortInUse.js && cross-env START_HOT=1 yarn start-web-dev
$ cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack-dev-server/bin/webpack-dev-server --config webpack.config.web.dev.js
Starting Web Process...
i 「wds」: Project is running at http://localhost:1111/
i 「wds」: webpack output is served from http://localhost:1111/dist
i 「wds」: Content not from webpack is served from C:\Users\Germain\Sites\presentations\dist
i 「wds」: 404s will fallback to /index.html
i 「wdm」: wait until bundle finished: /dist
i 「wdm」:
i 「wdm」: Compiled successfully.
i 「wdm」: Compiling...

But as soon as I edit a file inside the app/ folder:

C:\Users\Germain\Sites\presentations\node_modules\html-webpack-plugin\lib\compiler.js:111
          content: childCompilation.assets[outputName].source()
                                                       ^

TypeError: Cannot read property 'source' of undefined
    at childCompiler.runAsChild (C:\Users\Germain\Sites\presentations\node_modules\html-webpack-plugin\lib\compiler.js:111:56)
    at compile (C:\Users\Germain\Sites\presentations\node_modules\webpack\lib\Compiler.js:296:11)
    at hooks.afterCompile.callAsync.err (C:\Users\Germain\Sites\presentations\node_modules\webpack\lib\Compiler.js:553:14)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (C:\Users\Germain\Sites\presentations\node_modules\webpack\node_modules\tapable\lib\HookCodeFactory.js:24:12), <anonymous>:15:1)
    at AsyncSeriesHook.lazyCompileHook [as _callAsync] (C:\Users\Germain\Sites\presentations\node_modules\webpack\node_modules\tapable\lib\Hook.js:35:21)
    at compilation.seal.err (C:\Users\Germain\Sites\presentations\node_modules\webpack\lib\Compiler.js:550:30)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (C:\Users\Germain\Sites\presentations\node_modules\webpack\node_modules\tapable\lib\HookCodeFactory.js:24:12), <anonymous>:6:1)
    at AsyncSeriesHook.lazyCompileHook [as _callAsync] (C:\Users\Germain\Sites\presentations\node_modules\webpack\node_modules\tapable\lib\Hook.js:35:21)
    at hooks.optimizeAssets.callAsync.err (C:\Users\Germain\Sites\presentations\node_modules\webpack\lib\Compilation.js:1294:35)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (C:\Users\Germain\Sites\presentations\node_modules\webpack\node_modules\tapable\lib\HookCodeFactory.js:24:12), <anonymous>:6:1)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

The only solution I found was to change:

new webpack.HotModuleReplacementPlugin({
      multiStep: true
    }),

To false. But of course Hot reload requires a manual refresh in the browser.

Have you (or @Slapbox) encountered this error?

webpack.config.web.dev.js is here

Edit:

dbvrac commented 5 years ago

I almost have it. I used the process @Germain-Italic stepped through for dev., including the webpack.config.web.dev.js file and it works great! I love it! Note: import htmlLoader from 'html-loader'; doesn't seem to be used.

However, I'm still struggling with the build part. It builds (so it seems), but I'm not clear how to deploy it. I've tried serving it a few different ways with http-server but the best I manage to get is

console: GET http://localhost:8080/ [HTTP/1.1 200 OK 2ms] console: GET http://localhost:8080/dist/web.prod.js?cbf9d1a283858d08c31a [HTTP/1.1 404 Not Found 1ms]

germain-italic commented 5 years ago

@dbvrac could you post your prod webpack.config.prod.js?

germain-italic commented 5 years ago

On a second read: keep in mind that after build stage, neither Webpack nor Node provide any webserver that would serve your static built files.

Am a using an Apache virtualhost pointing into my ~/project/dist/web folder to serve them, index.html being generated by HtmlWebpackPlugin using index-web.html template (my html template is a bit different for the Electron app and for the web for some reason).

Is that what you are trying to achieve?

dbvrac commented 5 years ago

I haven't tried using Apache, and it isn't really my goal but if it works I'll use it. In the mean time, some context: MacOS 10.13.6, Node v10.12.0, npm 6.4.1, yarn 1.10.1, and I'm still using v0.14.0 of ERB.

My webpack.config.web.prod.js file is hosed, I'm sure. When I run yarn run build-web I'm not getting a ~/project/dist/ folder. I'm getting ~/project/app/dist/ and ~/project/app/www/ folders. Can you post your working version?

Here's what I've been working with. I've fiddled with it in different ways to no avail:

import path from 'path';
import webpack from 'webpack';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import merge from 'webpack-merge';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import UglifyJSPlugin from 'uglifyjs-webpack-plugin';
import baseConfig from './webpack.config.base';
import CheckNodeEnv from './internals/scripts/CheckNodeEnv';

CheckNodeEnv('production');

export default merge.smart(baseConfig, {
  devtool: 'source-map',

  mode: 'production',

  target: 'web',

  entry: './app/index',

  output: {
    path: path.join(__dirname, 'app/dist'),
    publicPath: './dist/',
    filename: 'web.prod.js'
  },

  module: {
    rules: [
      // Babel loader
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
            plugins: [
              // Here, we include babel plugins that are only required for the
              // renderer process. The 'transform-*' plugins must be included
              // before react-hot-loader/babel
              'transform-class-properties',
              'transform-es2015-classes',
              'react-hot-loader/babel'
            ]
          }
        }
      },
      // html-loader
      {
        test: /\.(html)$/,
        use: {
          loader: 'html-loader'
        }
      },
      // css-loader (global)
      {
        test: /\.global\.css$/,
        use: [
          {
            loader: 'style-loader'
          },
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      },
      // css-loader (others)
      {
        test: /^((?!\.global).)*\.css$/,
        use: [
          {
            loader: 'style-loader'
          },
          {
            loader: 'css-loader',
            options: {
              modules: true,
              sourceMap: true,
              importLoaders: 1,
              localIdentName: '[name]__[local]__[hash:base64:5]'
            }
          }
        ]
      },
      // SASS support - compile all .global.scss files and pipe it to style.css
      {
        test: /\.global\.(scss|sass)$/,
        use: [
          {
            loader: 'style-loader'
          },
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'sass-loader'
          }
        ]
      },
      // SASS support - compile all other .scss files and pipe it to style.css
      {
        test: /^((?!\.global).)*\.(scss|sass)$/,
        use: [
          {
            loader: 'style-loader'
          },
          {
            loader: 'css-loader',
            options: {
              modules: true,
              sourceMap: true,
              importLoaders: 1,
              localIdentName: '[name]__[local]__[hash:base64:5]'
            }
          },
          {
            loader: 'sass-loader'
          }
        ]
      },
      // WOFF Font
      {
        test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 10000,
            mimetype: 'application/font-woff'
          }
        }
      },
      // WOFF2 Font
      {
        test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 10000,
            mimetype: 'application/font-woff'
          }
        }
      },
      // TTF Font
      {
        test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 10000,
            mimetype: 'application/octet-stream'
          }
        }
      },
      // EOT Font
      {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        use: 'file-loader'
      },
      // SVG Font
      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 10000,
            mimetype: 'image/svg+xml'
          }
        }
      },
      // Common Image Formats
      {
        test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/,
        use: 'url-loader'
      }
    ]
  },

  optimization: {
    minimizer: [
      new UglifyJSPlugin({
        parallel: true,
        sourceMap: true
      }),
      new OptimizeCSSAssetsPlugin({
        cssProcessorOptions: {
          map: {
            inline: false,
            annotation: true
          }
        }
      })
    ]
  },

  plugins: [
    /**
     * Create global constants which can be configured at compile time.
     *
     * Useful for allowing different behaviour between development builds and
     * release builds
     *
     * NODE_ENV should be production so that modules do not perform certain
     * development checks
     */
    new webpack.EnvironmentPlugin({
      NODE_ENV: 'production'
    }),

    new MiniCssExtractPlugin({
      filename: 'style.css'
    }),

    new BundleAnalyzerPlugin({
      analyzerMode:
        process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled',
      openAnalyzer: process.env.OPEN_ANALYZER === 'true'
    }),

    new HtmlWebpackPlugin({
      hash: true,
      title: 'My Web application',
      myPageHeader: 'Hello World',
      template: './app/index.template.html',
      filename: '../www/index.html' // relative to root of the application
    })
  ]
});
germain-italic commented 5 years ago

HI @dbvrac sorry I couldn't reply before. The file works with Webpack latest!

It builds (so it seems)

Yes indeed, I'm getting the compiled files in the dist folder.

but I'm not clear how to deploy it

Confirmed, the files are for the web (which was my goal when I posted the example) so you can serve them using Apache or Nginx (I'm using Laragon on Windows with the built-in Apache server, and Apache on Ubuntu in production). So here's your web app.

For the Electron app, using the task "app:release" inside package.json (shortcut for yarn app:build && electron-builder), electron-builder creates an .exe inside release/app using the following config files :

# electron-builder.json
{
  "appId": "cartable.digital.app",
  "productName": "Presentations Colissimo",
  "copyright": "Groupe La Poste",
  "directories": {
    "buildResources": "src",
    "output": "release/app"
  },
  "win": {
    "target": "portable"
  }
}

electron-webpack.json

{
  "title": "Presentations Colissimo",
  "customTemplateFile": "src/templates/structure.html",
  "main": {
    "sourceDirectory": "src/electron-webpack"
  },
  "renderer": {
    "sourceDirectory": "src"
  }
}

Please note that I have edited electron-webpack in order to introduce the new customTemplateFile variable. I haven't been able to merge these edits in the project due to lack of knowledge on project contribution, however I have posted all the diffs in this issue: https://github.com/electron-userland/electron-webpack/issues/197.

dbvrac commented 5 years ago

@Germain-Italic Thanks for the info. Which version of ERB are you working with? Setting aside my n00b issues with Build for a moment, I had Dev running quite nicely. However, this was when my project was based on ERB v0.14.0. I have since brought my project up to v0.17.0 and I'm unable to get Dev to work. I updated the scripts in package.json to look in the new configs directory, but I'm uncertain how to get past this:

$yarn run dev-web
yarn run v1.10.1
$ cross-env START_HOT=1 node -r babel-register ./internals/scripts/CheckPortInUse.js && cross-env START_HOT=1 yarn start-web-dev
/Users/username/directory/project-name/node_modules/babel-register/node_modules/babel-core/lib/transformation/file/index.js:558
      throw err;
      ^

SyntaxError: /Users/username/directory/project-name/internals/scripts/CheckPortInUse.js: Unexpected token (6:12)
  4 | 
  5 | (function CheckPortInUse() {
> 6 |   const port: string = process.env.PORT || '1212';
    |             ^
  7 | 
  8 |   detectPort(port, (err: ?Error, availablePort: number) => {
  9 |     if (port !== String(availablePort)) {
    at Parser.pp$5.raise (/Users/username/directory/project-name/node_modules/babylon/lib/index.js:4454:13)
    at Parser.pp.unexpected (/Users/username/directory/project-name/node_modules/babylon/lib/index.js:1761:8)
    at Parser.pp$1.parseVar (/Users/username/directory/project-name/node_modules/babylon/lib/index.js:2342:12)
    at Parser.pp$1.parseVarStatement (/Users/username/directory/project-name/node_modules/babylon/lib/index.js:2169:8)
    at Parser.pp$1.parseStatement (/Users/username/directory/project-name/node_modules/babylon/lib/index.js:1861:19)
    at Parser.pp$1.parseBlockBody (/Users/username/directory/project-name/node_modules/babylon/lib/index.js:2268:21)
    at Parser.pp$1.parseBlock (/Users/username/directory/project-name/node_modules/babylon/lib/index.js:2247:8)
    at Parser.pp$3.parseFunctionBody (/Users/username/directory/project-name/node_modules/babylon/lib/index.js:4235:22)
    at Parser.pp$1.parseFunction (/Users/username/directory/project-name/node_modules/babylon/lib/index.js:2386:8)
    at Parser.pp$3.parseFunctionExpression (/Users/username/directory/project-name/node_modules/babylon/lib/index.js:3760:17)
error Command failed with exit code 1.
germain-italic commented 5 years ago

Hi @dbvrac my version is v0.14.0 I won't risk trying another version.

janakg commented 5 years ago

Hello friends, please see the changes required to enable web dev and production in a clean way for the latest release 0.17.

I have created PR for the same. Meanwhile, you can see the changes here. https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2136

AshwinDeTaeye commented 2 years ago

Hello, now the project has moved quit a bit since this discussion started. We can now run the boilerpate within the browser by default for development.

Is anyone now able to do the same within production? ( maybe with express ) So one .exe / .appimage that when installed runs the electron app, and host the same UI on localhost.

cglun commented 2 years ago

If I use ant Design, the packaged application route cannot work