Bundling for “Isolated Worlds” in Chrome Browser Extension

I’m thinking about how I can structure my code.

Since some code in Chrome Browser runs in “Isolated Worlds”, little VMs, I have trouble bundling my Javascript without creating a lot of duplicated code.

For my browser extension, I want to add a listener, that has a callback function someFunctionToUnpack.

// typescript
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === 'DO_STH') {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      const tab = tabs[0];
      if (tab && tab.id !== undefined) {
        chrome.scripting.executeScript({
          target: { tabId: tab.id as number },
          func: someFunctionToUnpack,
        });
      }
    });
  }
});

Ideally, it contains code that I can re-use in other places, because I do need this code in other places.

// typescript
import { some } from "anotherFile.ts";

const someFunctionToUnpack = ()=> {
  return some.objectContainingFunctions();
}
const doSomthingWithA = (a)=> {
  console.log(a);
}

const someFunctionToUnpack() {
  const a = anotherFunctionCall()
  doSomethingWithA(a);
}

If I did that, my current settings compile in a way that object() of some is not defined.

// In the VM, which has got the callback and
// is now isolated from the rest of the compiled javascript:
(()=>{
  const e = (()=>{
    const e = {
      some.objectContainingFunctions() // breaks here
    };
    console.log(e);
  })()
}
)()

While in a normal environment, we would bundle everything to the absolut minimum, I
require some code to be fully unpack and intentionally be duplicated, so that callback from event listeners in Chrome extensions can have access to their references, while I still avoid writing duplicated code.

Because it breaks in the VM, I’m forced to copy and move code into the someFunctionToUnpack and create duplicated code.

I’m using Webpack 5

module.exports = {
  entry: {
    'service-worker': './src/service-worker.ts',
    'content': './src/content.ts',
    'index': './src/backend/index.ts',
  },
  output: {
    path: path.resolve(__dirname, 'out'),
    filename: '[name].js',
    clean: true
  },
  resolve: {
    extensions: ['.ts', '.js', '.json']
  },
  module: {
    rules: [
      {
        test: /.ts$/,
        use: 'ts-loader',
        exclude: /node_modules/
      },
      {
        test: /.json$/,
        type: 'javascript/auto',
        loader: 'json-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
        template: './src/index.html',
        filename: 'index.html',
        chunks: ['index'],
      }),
    new CopyWebpackPlugin({
      patterns: [
        { from: 'src/manifest.json', to: 'manifest.json' },
        { from: 'src/icons', to: 'icons' },
        { from: 'src/styles', to: 'styles' },
      ]
    })
  ],
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin({
      terserOptions: {
        compress: true,
        mangle: true
      }
    })],
  },
};