Electron with webpack and css from node_modules

I’m trying to use electron and it works with CSS or SCSS that I create, but if I load a module that is importing css/scss it can’t do it. Any help would be much appreciated as I finally have a real need to use electron

import { AureliaPlugin } from 'aurelia-webpack-plugin';
import * as CleanWebpackPlugin from 'clean-webpack-plugin';
import * as HtmlWebpackPlugin from 'html-webpack-plugin';
import { resolve } from 'path';
import * as TerserPlugin from 'terser-webpack-plugin';
import * as webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import * as merge from 'webpack-merge';
import * as WriteFilePlugin from 'write-file-webpack-plugin';
import { CopyPkgJsonAndRemoveKeys, ElectronProcessPlugin } from './webpack-plugins';

const isDevServer = process.argv.some(v => v.includes('webpack-dev-server'));
const appRoot = process.cwd();

const cssRules = [{ loader: 'css-loader' }];

function getBundleAnalyzerPlugin(entryPoints: webpack.Entry): BundleAnalyzerPlugin {
  return new BundleAnalyzerPlugin({
    reportFilename: `bundle-report-${Object.keys(entryPoints)[0]}.html`,
    analyzerMode: 'static',
    openAnalyzer: false
  });
}

function when(condition: boolean, ...whenTrue: webpack.Plugin[]): webpack.Plugin[] {
  return condition ? whenTrue : [];
}

const commonConfig: ({ production }) => webpack.Configuration = (
  { production } = { production: false }
) => ({
  mode: production ? 'production' : 'development',
  devtool: production ? 'source-map' : 'inline-source-map',
  devServer: {
    disableHostCheck: true
  },
  resolve: {
    mainFields: ['module', 'main'],
    extensions: ['.ts', '.js'],
    modules: ['src', 'node_modules'].map(x => resolve(x))
  },
  output: {
    libraryTarget: 'commonjs2'
  },
  optimization: {
    minimize: production,
    concatenateModules: false,
    minimizer: [
      new TerserPlugin({
        cache: true,
        parallel: true,
        sourceMap: true,
        terserOptions: {
          output: {
            comments: false
          }
        }
      })
    ],
    namedModules: true
  },
  performance: { hints: false },
  module: {
    rules: [
      {
        test: /\.css$/i,
        issuer: [{ not: [{ test: /\.html$/i }] }],
        use: ['style-loader', ...cssRules]
      },
      {
        test: /\.css$/i,
        issuer: [{ test: /\.html$/i }],
        // CSS required in templates cannot be extracted safely
        // because Aurelia would try to require it again in runtime
        use: cssRules
      },
      {
        // Relocates assets that are located dynamically at runtime and rewrites
        // their location. This is particularly helpful for node binaries
        // located via the bindings or node-pre-gyp libraries.
        test: /\.(m?js|node)$/,
        parser: { amd: false },
        use: {
          loader: '@zeit/webpack-asset-relocator-loader',
          options: {
            outputAssetBase: 'assets'
          }
        }
      },
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
        issuer: /\.[tj]s$/i
      },
      {
        test: /\.scss$/,
        use: ['css-loader', 'sass-loader'],
        issuer: /\.html?$/i
      },
      {
        test: /\.[tj]s$/i,
        loader: 'ts-loader',
        exclude: /node_modules/
      },
      {
        test: /\.html$/i,
        use: [
          {
            loader: 'html-loader',
            options: {
              minimize: true
            }
          }
        ]
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf|png|svg)$/,
        use: [
          {
            loader: 'file-loader',
            options: { name: 'assets/[name].[ext]' }
          }
        ]
      }
    ]
  },
  plugins: [
    new ElectronProcessPlugin(),
    new webpack.DefinePlugin({
      'process.env.production': JSON.stringify(production)
    })
  ],
  node: false,
  externals: ['electron']
});

export const main = (entryPoints: webpack.Entry) => ({ production } = { production: false }) =>
  merge.smart(commonConfig({ production }), {
    entry: entryPoints,
    target: 'electron-main',
    plugins: [
      new CopyPkgJsonAndRemoveKeys(),
      ...when(
        production,
        getBundleAnalyzerPlugin(entryPoints),
        new CleanWebpackPlugin(['dist'], { root: appRoot, verbose: false })
      )
    ]
  });

export const preload = (entryPoints: webpack.Entry) => ({ production } = { production: false }) =>
  merge.smart(commonConfig({ production }), {
    entry: entryPoints,
    target: 'electron-renderer',
    plugins: [...when(production, getBundleAnalyzerPlugin(entryPoints))]
  });

export const renderer = (entryPoints: webpack.Entry, nodeIntegration: boolean = true) => (
  { production } = { production: false }
) =>
  merge.smart(commonConfig({ production }), {
    entry: entryPoints,
    target: nodeIntegration ? 'electron-renderer' : 'web',
    output: {
      filename: production ? '[name].[contenthash:8].js' : '[name].[hash:8].js',
      libraryTarget: nodeIntegration ? 'commonjs2' : 'this'
    },
    optimization: {
      splitChunks: {
        chunks: 'all',
        maxInitialRequests: Infinity
      }
    },
    plugins: [
      new AureliaPlugin({ aureliaApp: undefined, features: { polyfills: 'esnext' } }),
      ...when(production, getBundleAnalyzerPlugin(entryPoints)),
      ...when(isDevServer, new WriteFilePlugin()),
      // Create an HTML file for each entry point
      ...Object.keys(entryPoints).map(
        entry => new HtmlWebpackPlugin({ title: '', filename: `${entry}.html`, chunks: [entry] })
      )
    ]
  });

1 Like

I think the config looks fine, so maybe we will need more specific error msg. What error did you get?

This is from electron dev tools. Unfortunately this is all I get

Error: Failed loading required CSS file: @syncfusion/ej2/material.css
    at fixupCSSUrls (vendors~renderer.da0f30d1.js:257910)
    at vendors~renderer.da0f30d1.js:257944

This is being loaded from a plugin that imports it. This plugin works properly in a standard Aurelia app

1 Like

I hope someone has an answer. This started off from @timfish template with css being added.

So close to having something usable

1 Like

@elitemike did you manage to solve this? If you share a repository with me I’ll be happy to take a look.

1 Like

@elitemike, I fought with same problem using EJ2 components. It’s about how you reference the css file. With , from scss file? As I remember, the solution was to . Looks strange, but it works.

1 Like

I’ll have to see if I still have a project. I ultimately moved on from trying to get webpack working for this scenario. Sadly electron has taken a back seat to my work again.

1 Like

Hi everyone !

I think i’m having the same issue over here. I’m using Syncfusion EJ2 as well and I struggle to load their style sheet.

Their stylesheets load fine when im using developement config however. Im having issues only regarding the PROD configuration.

I will share config files with you guys. But please note that I’m using Lerna and the node_modules folder that contains @Syncfusion is at the root of the project (above) the one im building. If its relevant to my issue …

The issue started only when we start using stylesheets import directly in our Typescript :
import "@syncfusion/ej2-buttons/styles/material.scss";

this syntax works great. I’ve had a few adjsutment made in the webpack.config.js as well

 module: {
            rules: [
                { test: /[^.]*\.html/i, loader: "html-loader" },
                { test: /\.ts$/i, loader: "ts-loader", options: { ignoreDiagnostics: [2769] }, exclude: ["/node_modules/", "/src/shared-components/"] },
                { test: /\.css$/i, issuer: [{ not: [{ test: /\.html$/i }] }], use: ["style-loader", ...cssRules] },
                { test: /\.css$/i, issuer: [{ test: /\.html$/i }], use: cssRules },
                {
                    test: /\.scss$/,
                    use: [
                        {
                            loader: "style-loader",
                        },
                        {
                            loader: "css-loader",
                            options: {
                                sourceMap: mode === "production" ? false : true,
                            },
                        },
                        {
                            loader: "resolve-url-loader",
                        },
                        {
                            loader: "sass-loader",
                            options: {
                                sourceMap: mode === "production" ? false : true,
                                sassOptions: {
                                    includePaths: [path.resolve(__dirname, "../../node_modules/@syncfusion")],
                                },
                            },
                        },
                        {
                            loader: "sass-resources-loader",
                            options: {
                                sourceMap: mode === "production" ? false : true,
                                resources: path.resolve(__dirname, "src/shared-components/common/style/constants-v2.scss"),
                            },
                        },
                    ],
                },
            ],
        },


as you can see the prod env. (left) doesn’t load all the style, the dev. env however looks fine (right)

1 Like