Search code examples
sqlitewebpackelectronsqlcipherelectron-forge

Setting up sqlcipher in an electron app using electron-forge and webpack


I am trying to open a sqlite3 database encrypted with sqlcipher. I have the password and I could open it successfully using sqlitebrowser.

I started my project using this template. It is based on the electron-forge webpack plugin.

When I type yarn start, it creates a .webpack folder and all the compiled code goes in it. When I stop the terminal command, this folder disappears.

Then I wanted to use the package @journeyapps/sqlcipher to open my database, but this line is causing an error in the main process: const sqlite3 = require("@journeyapps/sqlcipher")

The error is: Error: Cannot find module '<<my_path>>/.webpack/main/native_modules/lib/binding/napi-v6-linux-x64/node_sqlite3.node'


The package documentation says two things about using it with electron-forge:

  1. make sure that the folder node_modules/@journeyapps/sqlcipher/lib/binding/napi-v6-linux-x64 exists -> yes it exists

  2. Disable rebuilding of this library using the onlyModules option of electron-rebuild in your package.json "config": { "forge": { "electronRebuildConfig": { "onlyModules": [] // Specify other native modules here if required } } -> I did it, I added the lines of code

I still have the error, but I feel it can be solved "easily" (with a lot more understanding of webpack than I have). Indeed, a solution would be to move the folder binding/napi-v6-linux-x64 into .webpack each time I launch the app, right?


I tried to do electron-rebuild -f -w sqlite3, the rebuild succeeded but nothing happens, I still have the same error.


I am stuck here and cannot go further for my app, as it lays on reading this database. Should I start a new project and avoid using webpack? Do you have any example of a project where this package is successfully imported and used?

Thank you in advance for your help!


Solution

  • Well, I finally figured it out. Actually, I tried so much little changes but I managed to make the app work (nearly) as expected.

    How I found the solution

    First: this doesn't have anything to do with the library sqlcipher itself. In fact, it's a webpack configuration problem while dealing with native libraries.

    I started a fresh minimal install of electron-forge with Webpack and Typescript template (npx create-electron-app test-electron-forge-github --template=typescript-webpack) and added sqlite3 and @journeyapps/sqlcipher. It worked so I made a lot of changes in my Webpack configuration to make it closer with the electron-forge one.

    Warning

    The changes I made broke Redux. I chose to sacrifice Redux in order to make sqlcipher work, as today I didn't find a solution for both of them to work.

    Old Webpack configuration files

    main.webpack.js

    module.exports = {
      resolve: {
        extensions: ['.ts', '.js']
      },
      entry: './electron/main.ts',
      module: {
        rules: require('./rules.webpack'),
      }
    }
    

    renderer.webpack.js

    module.exports = {
      resolve: {
        extensions: ['.ts', '.tsx', '.js']
      },
      module: {
        rules: require('./rules.webpack'),
      },
    }
    

    rules.webpack.js

    module.exports = [
        {
            test: /\.node$/,
            use: 'node-loader',
        },
        {
            test: /\.(m?js|node)$/,
            parser: { amd: false },
            use: {
                loader: '@marshallofsound/webpack-asset-relocator-loader',
                options: {
                    outputAssetBase: 'native_modules',
                },
            },
            resolve: {
                fullySpecified: false,
            }
        },
        {
            test: /\.(js|ts|tsx)$/,
            exclude: /node_modules/,
            use: {
                loader: 'babel-loader',
            }
        },
        {
            test: /\.(png|jpe?g|gif)$/i,
            loader: 'file-loader',
            options: {
                name: '[path][name].[ext]',
            },
        },
        {
            test: /\.(sass|less|css)$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-loader',
                'sass-loader',
            ]
        },
    ]
    

    New configuration

    main.webpack.js

    module.exports = {
        resolve: {
            extensions: ['.ts', '.js']
        },
        entry: './electron/main.ts',
        module: {
            rules: [
                ...require('./rules.webpack'),
    
                {
                    test: /\.(m?js|node)$/,
                    parser: { amd: true },
                    use: {
                        loader: '@vercel/webpack-asset-relocator-loader',
                        options: {
                            outputAssetBase: 'native_modules',
                            emitDirnameAll: true,
                        },
                    }
                },
            ],
        }
    }
    

    The native modules configuration has been moved from rules.webpack.js to main.webpack.js. This is due to a bug happening in the renderer process if the rule stayed in the rules.wepback.ts file. The window would open but would stay blank. In the console, there would be an error: __dirname is not defined. See this Github issue from where I took the solution.

    Also note that I changed the loader as the previous one was not doing properly its job.

    renderer.webpack.js Unchanged

    rules.webpack.js

    module.exports = [
        {
            // We are specifying native_modules in the test because the asset relocator loader generates a
            // "fake" .node file which is really a cjs file.
            test: /native_modules\/.+\.node$/,
            use: 'node-loader',
        },
        {
            test: /\.(js|ts|tsx)$/,
            exclude: /node_modules/,
            use: {
                loader: 'babel-loader',
            }
        },
        {
            test: /\.(png|jpe?g|gif)$/i,
            loader: 'file-loader',
            options: {
                name: '[path][name].[ext]',
            },
        },
        {
            test: /\.(sass|less|css)$/,
            use: [
                'style-loader',
                'css-loader',
                'postcss-loader',
                'sass-loader',
            ]
        },
    ]
    

    Redux

    I had to remove this property from rules.webpack.js:

    resolve: {
        fullySpecified: false,
    }
    

    This line was making Redux work.

    Conclusion

    I don't really know how this works, but it works. I hope it will help you if you are struggling as I was.