Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webpack issue using extra npm packages #52

Open
DenisTimofijuk opened this issue Feb 22, 2023 · 3 comments
Open

Webpack issue using extra npm packages #52

DenisTimofijuk opened this issue Feb 22, 2023 · 3 comments

Comments

@DenisTimofijuk
Copy link

DenisTimofijuk commented Feb 22, 2023

I am using inject technology to be able to access window object from my content scripts as per this example: https://gist.github.com/devjin0617/3e8d72d94c1b9e69690717a219644c7a
Everything was working fine until I added npm seedrandom to my inject file. Now when I build my project, inject file is modified in such a way that it no longer is working as it should. Tried various webpack settings, but with no luck. Should this be working by default using such a prebuild environment?

 "dependencies": {
    "seedrandom": "^3.0.5"
  },
  "devDependencies": {
    "@types/chrome": "0.0.158",
    "@types/jquery": "^3.5.14",
    "@types/seedrandom": "^3.0.4",
    "copy-webpack-plugin": "^9.0.1",
    "glob": "^7.1.6",
    "prettier": "^2.2.1",
    "rimraf": "^3.0.2 ",
    "ts-loader": "^8.0.0",
    "typescript": "^4.4.3 ",
    "webpack": "^5.0.0",
    "webpack-cli": "^4.0.0",
    "webpack-merge": "^5.0.0"
  }


module.exports = {
    entry: {
      popup: path.join(srcDir, 'popup/popup.ts'),
      options: path.join(srcDir, 'options/options.ts'),
      background: path.join(srcDir, 'background/background.ts'),
      content: path.join(srcDir, 'content/content.ts'),
      inject: path.join(srcDir, 'inject/inject.ts'),
    //   vendor_inject: ['seedrandom'],
    },
    output: {
        path: path.join(__dirname, "../dist/js"),
        filename: "[name].js",
    },
    optimization: {
        splitChunks: {
            // name: "vendor",
            // chunks(chunk) {
            //   return chunk.name !== 'background';
            // },
            cacheGroups: {
                vendor: {
                  name: 'vendor',
                  chunks(chunk) {
                      return chunk.name !== 'background';
                    },
                },
                vendor_inject: {
                  test: /[\\/]node_modules[\\/](seedrandom)[\\/]/,
                  name: 'vendor_inject',
                  chunks: 'all'
                }
              }
        },
    },
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: "ts-loader",
                exclude: /node_modules/,
            },
        ],
    },
    resolve: {
        extensions: [".ts", ".tsx", ".js"],
    },
    plugins: [
        new CopyPlugin({
            patterns: [{ from: ".", to: "../", context: "public" }],
            options: {},
        }),
    ],
};

@DenisTimofijuk
Copy link
Author

My progress with ChatGPT (no luck):

WEBPACK CONFIGURATION:

const webpack = require("webpack");
const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");
const srcDir = path.join(__dirname, "..", "src");

module.exports = {
    entry: {
      popup: path.join(srcDir, 'popup/popup.ts'),
      options: path.join(srcDir, 'options/options.ts'),
      background: path.join(srcDir, 'background/background.ts'),
      content: path.join(srcDir, 'content/content.ts'),
      inject: path.join(srcDir, 'inject/inject.ts'),
    },
    output: {
        path: path.join(__dirname, "../dist/js"),
        filename: "[name].js",
    },
    optimization: {
        splitChunks: {
            name: "vendor",
            chunks(chunk) {
              return chunk.name !== 'background';
            },
        },
    },
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: "ts-loader",
                exclude: /node_modules/,
            },
        ], 
    },
    resolve: {
        extensions: [".ts", ".tsx", ".js"],
    },
    plugins: [
        new CopyPlugin({
            patterns: [{ from: ".", to: "../", context: "public" }],
            options: {},
        }),
        new webpack.ProvidePlugin({
            seedrandom: 'seedrandom'
          })
    ],
};

NumericList.ts:


import Compositor from "../../Compositor";
import { handleIgnore } from "../../global BOT rules";
import { getAvailableAnswers, getSimpleText, randomIntFromInterval, warnUnhandled } from "../../global helpers";
import { getMin, getMax, getCurrentSum } from "./Getters";
import { collectSermoRangeValues, setNumericValuesBySermoRange, handleAnswersForTotalSumWithRanges } from "./rulesHandlers";
import { multiSumEequal, multiSumMax, multiSumMin } from "./sumGenerators";
const compositor = Compositor.getInstance();
const PROTOCOL_MAX_TRIES = 100;
const NUM_QUARTERS = 4;

// . . . 
// removed some code for simplicity 
// . . . 

export function shuffleQuarters(respondentId:number) {
  const quarterSize = PROTOCOL_MAX_TRIES / NUM_QUARTERS;
  debugger;
  // @ts-ignore
  const rng = seedrandom(respondentId.toString()); // Use the seedrandom library for consistent random numbers

  // Generate a shuffled list of quarter indices
  const quarterIndices = Array.from({length: NUM_QUARTERS}, (_, i) => i);
  //shuffleArray(quarterIndices, rng);

  // Generate a shuffled list of indices within each quarter
  const shuffledIndices = [];
  for (let i = 0; i < NUM_QUARTERS; i++) {
    const quarterStart = quarterIndices[i] * quarterSize;
    const indices = Array.from({length: quarterSize}, (_, j) => quarterStart + j);
    // shuffleArray(indices, rng);
    shuffledIndices.push(...indices);
  }

  return shuffledIndices;
}

// @ts-ignore
function shuffleArray(arr:Array<any>, rng:seedrandom.PRNG) {
  for (let i = arr.length - 1; i > 0; i--) {
    const j = Math.floor(rng() * (i + 1));
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }
}


Inject.ts:


import { initMain } from "./local_content/init_main";
import { loadRules } from "./local_content/loadRules";
import "./local_content/collectValidationMessages";
import shuffleQuarters from "./handlers/NumericList/NumericList";

declare namespace chrome {
    let it_is_not_supported_here: null;
}

console.log('Preparing inject.js')

if (window.Confirmit && window.Confirmit.page) {
    loadRules();
    initMain();
    shuffleQuarters('myID');
    console.log('%c Inject.js ready. ', 'background: #00EAD7; color: #EA0013');
}

As you can see, inject.ts is self-invoking since it is an entry file. Once I use this code ### const rng = seedrandom(respondentId.toString()); ### in NumericList.ts file, after building with webpack inject.ts is no longer self-invoking. How to fix this?

@DenisTimofijuk
Copy link
Author

Created a demonstration project: https://github.com/DenisTimofijuk/injectIssueExample
Once I enabling rng = seedrandom('MyID'); in the inject.ts file, this file no longer self-invoking and that is the issue for me. How to solve it?

@ale-ben
Copy link

ale-ben commented Oct 31, 2023

I might have some updates on this.
First of all, this was my situation:

content_script.tsx:

import { sha256 } from "js-sha256";

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
	if (sender.tab === undefined && request.event && request.event === 'toggle')
		console.log(sha256("Hello world"));
	sendResponse({ farewell: 'goodbye' });
});

console.log('content_script.tsx loaded');

background.ts:

chrome.action.onClicked.addListener(async (tab) => {
	chrome.tabs
		.sendMessage(tab.id ? tab.id : -1, { event: 'toggle' })
		.catch((error) => {
			console.error(error);
		});
});

Content script has a console log as soon as it is loaded.
When I added console.log(sha256("Hello world"));, the file stopped loading (no more console.log).
Note that this only happened with npm installed packages, had no problem with import on my files.

I found 2 things:

1.

Moving the import and the console.log(sha....) from context script to background solved everything. This would work but imply sending a message to background every time I had to generate SHA of something.

2.

Seems like content script does not work well with standard import / export. Using dynamic import for npm packages seems to solve the issue (see this answer on Stackoverflow.
IMPORTANT: If you follow this option, READ THE NOTE AT THE END OF THIS POST.

After using dynamic import, my content script looks like this:

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
	if (sender.tab === undefined && request.event && request.event === 'toggle')
		const sha256 = await import('js-sha256');
	        console.log(sha256.sha256("Hello world"));
	sendResponse({ farewell: 'goodbye' });
});

console.log('content_script.tsx loaded');

And everything works as intended.

NOTE:
Using dynamic imports resulted in the following error: Uncaught Error: Automatic publicPath is not supported in this browser

This can be easily solved by changing in webpack.common.js the output section from

output: {
        path: path.join(__dirname, "../dist/js"),
        filename: "[name].js"
    },

to

output: {
        path: path.join(__dirname, "../dist/js"),
        filename: "[name].js",
       publicPath: ''
    },

Also you have to update module in tsconfig.json from "module": "ES6", to "module": "ES2020",

UPDATE: dynamic import seems to lead to a policy violation: content_script.js:972 Refused to load the script 'http://192.168.1.22:5500/docs/vendor.js' because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:*". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback. .
Still figuring out how to solve this

UPDATE 2:
Apparently Content Security Policies are a pain and chrome does not allow to add other sources so it seems that the only options are either copy the source code in the src folder or use message passing

UPDATE 3:
Got to the conclusion that Webopack works like shit in this context, migrated project a VITE implementation that offers realtime reload (and it works perfectly) and out of the box import support.
You can find it here: https://crxjs.dev/vite-plugin https://github.com/crxjs/chrome-extension-tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants