Search code examples
javascriptsveltevercelsveltekitgun

Deploying sveltekit app with gunjs on vercel throws cannot find module './lib/text-encoding'


I have a sveltekit app in which i use gunjs as a db with user auth etc... (This error problem didnt exist before i added gunjs auth)

The problem now is that when i use vercel to deploy the app it throws this (500: INTERNAL_SERVER_ERROR):

`Unhandled Promise Rejection    {"errorType":"Runtime.UnhandledPromiseRejection","errorMessage":"Error: Cannot find module './lib/text-encoding'\nRequire stack:\n- /var/task/node_modules/gun/sea.js","reason":{"errorType":"Error","errorMessage":"Cannot find module './lib/text-encoding'\nRequire stack:\n- /var/task/node_modules/gun/sea.js","code":"MODULE_NOT_FOUND","requireStack":["/var/task/node_modules/gun/sea.js"],"stack":["Error: Cannot find module './lib/text-encoding'","Require stack:","- /var/task/node_modules/gun/sea.js","    at Module._resolveFilename (node:internal/modules/cjs/loader:1134:15)","    at Module._load (node:internal/modules/cjs/loader:975:27)","    at exports.b (/var/task/___vc/__launcher/chunk-5UAC7W5H.js:1:1033)","    at /var/task/___vc/__launcher/bridge-server-BGIDXK2J.js:1:1443","    at Function.Re (/var/task/___vc/__launcher/bridge-server-BGIDXK2J.js:1:1809)","    at e.<computed>.L._load (/var/task/___vc/__launcher/bridge-server-BGIDXK2J.js:1:1413)","    at Module.require (node:internal/modules/cjs/loader:1225:19)","    at require (node:internal/modules/helpers:177:18)","    at USE (/var/task/node_modules/gun/sea.js:5:17)","    at /var/task/node_modules/gun/sea.js:189:44"]},"promise":{},"stack":["Runtime.UnhandledPromiseRejection: Error: Cannot find module './lib/text-encoding'","Require stack:","- /var/task/node_modules/gun/sea.js","    at process.<anonymous> (file:///var/runtime/index.mjs:1276:17)","    at process.emit (node:events:529:35)","    at emit (node:internal/process/promises:149:20)","    at processPromiseRejections (node:internal/process/promises:283:27)","    at process.processTicksAndRejections (node:internal/process/task_queues:96:32)"]}`

It works when i run it locally with npm run dev My db init looks like this:

`import { writable } from 'svelte/store';
import Gun from "gun"
import "gun/sea"
import "gun/axe"

export const db = new Gun();
export const user = db.user().recall({sessionStorage: true}); 

export const username = writable('');

user.get('alias').on((v:string) => username.set(v))

db.on('auth', async() => {
const alias = await user.get('alias');
username.set(alias)
})`

When i look into the node_modules/gun/lib folder the text-encoding module is the only one which has a folder instead of the .js module file, in the folder there is the index.js file which contains the modules code... but this should not be the problem...

Maybe i create the db the wrong way idk...?

I tried installing the text-encoding module which simply resulted in more errors, i tried diffrent gunjs versions which changed nothing, i fixed the node version to 18.20 which also changed nothing...

Please help :/

Edit: I also found that a diffrent library called shin.js uses the text-encoding module but imports it via require which i think doensnt work if the module is a file...

Edit 2: The sea.js file also gets the module with require which the original error also originates from... There is also a issue on the gunjs github about this same error but using nextjs: issue


Solution

  • After looking threw the errors i thinks the easiest solution is to replace the initialization of the text-encoding module in the sea.js file by passing in the instances over the global. variables...

    I wrote a postinstall script that just replaces those lines in the sea.js file:

    const fs = require('fs');
    
    const filePath = 'node_modules/gun/sea.js'; // Sea module to edit
    
    const oldTextDecoder = 'api.TextDecoder = TextDecoder;';
    const newTextDecoder = 'api.TextDecoder = global.TextDecoder;';
    const oldTextEncoder = 'api.TextEncoder = TextEncoder;';
    const newTextEncoder = 'api.TextEncoder = global.TextEncoder;';
    const oldRequire = 'const { TextEncoder, TextDecoder }';
    const newRequire = '//const { TextEncoder, TextDecoder }';
    
    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) {
        console.error(err);
      } else {
        const modifiedContent = data.replace(oldTextDecoder, newTextDecoder)
                                    .replace(oldTextEncoder, newTextEncoder)
                                    .replace(oldRequire, newRequire);
        fs.writeFile(filePath, modifiedContent, 'utf8', (err) => {
          if (err) {
            console.error(err);
          } else {
            console.log('Replaced the text-encoder definition and imports with global instances.');
          }
        });
      }
    });
    

    I pass in the global instances on my user/auth script: (+ all the needed imports)

    import Buffer from "buffer";
    import pkg from 'text-encoding';
    const {TextDecoder, TextEncoder} = pkg;
    global.Buffer = global.Buffer || Buffer.Buffer;
    global.TextEncoder = TextEncoder;
    global.TextDecoder = TextDecoder;
    import Gun from "gun/gun"
    import "gun/sea"