diff options
| author | Arpit Chakladar <arpitchakladar+git@gmail.com> | 2026-07-13 23:32:26 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-13 23:32:26 +0530 |
| commit | ce401d03e75bcbfd1cdfa963954e0807219fb80d (patch) | |
| tree | 52a813c1705a952312c20c328b00fa845a3453f9 /config/webpack | |
| parent | 98e7019c61ea0ffe344ef7ce809d38d62254a9fc (diff) | |
| parent | dfba7ff6fff7bde33b40a343a2318b35c429b40f (diff) | |
| download | banglar-bhumi-utils-ce401d03e75bcbfd1cdfa963954e0807219fb80d.tar.gz banglar-bhumi-utils-ce401d03e75bcbfd1cdfa963954e0807219fb80d.zip | |
Merge pull request #6 from arpitchakladar/adding-documentation-comments
Adding documentation comments
Diffstat (limited to 'config/webpack')
| -rw-r--r-- | config/webpack/loader/arrange-shared-module-loader.js | 16 | ||||
| -rw-r--r-- | config/webpack/loader/count-imports-loader.js | 9 | ||||
| -rw-r--r-- | config/webpack/loader/to-string-loader.js | 12 | ||||
| -rw-r--r-- | config/webpack/plugins/create-injected-shared-modules-webpack-plugin.js | 25 | ||||
| -rw-r--r-- | config/webpack/plugins/create-manifest-webpack-plugin/index.js | 48 | ||||
| -rw-r--r-- | config/webpack/plugins/create-rules-webpack-plugin.js | 16 | ||||
| -rw-r--r-- | config/webpack/plugins/inject-script-webpack-plugin.js | 23 | ||||
| -rw-r--r-- | config/webpack/utils/build-file.js | 12 | ||||
| -rw-r--r-- | config/webpack/utils/injected-code.js | 22 | ||||
| -rw-r--r-- | config/webpack/utils/inline-javascript.js | 11 | ||||
| -rw-r--r-- | config/webpack/utils/script-runtime.js | 13 | ||||
| -rw-r--r-- | config/webpack/utils/scripts.js | 14 | ||||
| -rw-r--r-- | config/webpack/utils/shared-modules.js | 12 |
13 files changed, 192 insertions, 41 deletions
diff --git a/config/webpack/loader/arrange-shared-module-loader.js b/config/webpack/loader/arrange-shared-module-loader.js index 2265185..63fd62d 100644 --- a/config/webpack/loader/arrange-shared-module-loader.js +++ b/config/webpack/loader/arrange-shared-module-loader.js @@ -1,8 +1,18 @@ import path from "path"; + import { ImportManager } from "import-manager"; import sharedModules from "../utils/shared-modules.js"; +/** + * Webpack loader that arranges shared modules in dependency order. + * As each shared module is processed, its own shared-module dependencies + * are inserted before it in the global `sortedSharedModules` array so that + * the final output respects the import graph. + * + * @param {string} source - The source code of the current module. + * @returns {string} The unmodified source (this loader is side-effect only). + */ export default function(source) { const currentSharedModuleName = path.basename(this.resourcePath.substring(0, this.resourcePath.length - 3)); @@ -17,7 +27,11 @@ export default function(source) { if (moduleIndex >= 0) { const currentSharedModuleName = sharedModules[moduleIndex]; - if (!(sortedSharedModules.includes(currentSharedModuleName) || modulesToBeIncluded.includes(currentSharedModuleName))) { + const alreadyIncluded = ( + sortedSharedModules.includes(currentSharedModuleName) + || modulesToBeIncluded.includes(currentSharedModuleName)); + + if (!alreadyIncluded) { modulesToBeIncluded.push(currentSharedModuleName); } } diff --git a/config/webpack/loader/count-imports-loader.js b/config/webpack/loader/count-imports-loader.js index 2586332..ef832bf 100644 --- a/config/webpack/loader/count-imports-loader.js +++ b/config/webpack/loader/count-imports-loader.js @@ -1,6 +1,15 @@ import { ImportManager } from "import-manager"; + import sharedModules from "../utils/shared-modules.js"; +/** + * Webpack loader that counts how many times each shared module is imported. + * This count is used later to determine which shared modules need to be + * included in the compiled output. + * + * @param {string} source - The source code of the current module. + * @returns {string} The unmodified source (this loader is side-effect only). + */ export default function(source) { const { sharedModulesImportedCount } = this.getOptions(); const manager = new ImportManager(source); diff --git a/config/webpack/loader/to-string-loader.js b/config/webpack/loader/to-string-loader.js index 248574a..025ea5e 100644 --- a/config/webpack/loader/to-string-loader.js +++ b/config/webpack/loader/to-string-loader.js @@ -1,7 +1,17 @@ +/** + * Webpack loader that converts HTML template files into JavaScript modules. + * It escapes template literals and replaces `$name$` placeholders with + * `${name}` interpolation syntax so the exported function can accept a + * replacements object. + * + * @param {string} source - Raw HTML file content. + * @returns {string} A JS module that exports a function accepting + * replacement values and returning the interpolated HTML string. + */ export default function(source) { source = source .replaceAll("\\", "\\\\") - .replaceAll("`","\\`") + .replaceAll("`", "\\`") .replaceAll("${", "\\${"); const replacements = {}; diff --git a/config/webpack/plugins/create-injected-shared-modules-webpack-plugin.js b/config/webpack/plugins/create-injected-shared-modules-webpack-plugin.js index 695d4ef..c94ae86 100644 --- a/config/webpack/plugins/create-injected-shared-modules-webpack-plugin.js +++ b/config/webpack/plugins/create-injected-shared-modules-webpack-plugin.js @@ -1,31 +1,44 @@ import path from "path"; -import fs from "fs"; + import webpack from "webpack"; import { getFileName } from "../utils/build-file.js"; +/** + * Webpack plugin that generates an injected shared-modules bundle. + * After all assets are processed, it creates a small JS file that + * calls the script injector for each shared module that was imported + * by injected scripts. + */ class CreateInjectedSharedModulesPlugin { + /** + * @param {{ injectedSharedModulesImportedCount: Record<string, number>, sortedSharedModules: string[] }} options + */ constructor({ injectedSharedModulesImportedCount, sortedSharedModules }) { this.injectedSharedModulesImportedCount = injectedSharedModulesImportedCount; this.sortedSharedModules = sortedSharedModules; } + /** + * @param {import("webpack").Compiler} compiler + * @returns {void} + */ apply(compiler) { - compiler.hooks.compilation.tap("CreateInjectedSharedModulesPlugin", compilation => { + compiler.hooks.compilation.tap("CreateInjectedSharedModulesPlugin", (compilation) => { compilation.hooks.processAssets.tap( { name: "CreateInjectedSharedModulesPlugin", stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL }, - assets => { + (_assets) => { const injectedSharedScripts = this.sortedSharedModules - .filter(sharedModule => this.injectedSharedModulesImportedCount[sharedModule] > 0) - .map(sharedModule => `shared/${getFileName(sharedModule, "shared")}.js`); + .filter((sharedModule) => this.injectedSharedModulesImportedCount[sharedModule] > 0) + .map((sharedModule) => `shared/${getFileName(sharedModule, "shared")}.js`); if (injectedSharedScripts.length > 0) { const scriptInjectorModuleName = getFileName("script-injector", "shared", true); const injectionCode = injectedSharedScripts - .map(injectedScript => `$${scriptInjectorModuleName}.injectScriptHead("shared/${path.basename(injectedScript)}");`) + .map((injectedScript) => `$${scriptInjectorModuleName}.injectScriptHead("shared/${path.basename(injectedScript)}");`) .join(""); compilation.emitAsset( `shared/${getFileName("injected-shared-modules", "shared")}.js`, diff --git a/config/webpack/plugins/create-manifest-webpack-plugin/index.js b/config/webpack/plugins/create-manifest-webpack-plugin/index.js index 6f1e2be..95d0be8 100644 --- a/config/webpack/plugins/create-manifest-webpack-plugin/index.js +++ b/config/webpack/plugins/create-manifest-webpack-plugin/index.js @@ -1,31 +1,53 @@ -import webpack from "webpack"; -import path from "path"; import fs from "fs"; +import path from "path"; + +import webpack from "webpack"; -import { getScriptRuntimeFromType } from "../../utils/script-runtime.js"; import { getFileName } from "../../utils/build-file.js"; +import { getScriptRuntimeFromType } from "../../utils/script-runtime.js"; import scripts from "../../utils/scripts.js"; + const manifest = JSON.parse( fs.readFileSync( - path.resolve("config/webpack/plugins/create-manifest-webpack-plugin/manifest-template.json"), - ), + path.resolve("config/webpack/plugins/create-manifest-webpack-plugin/manifest-template.json") + ) ); +/** + * Webpack plugin that dynamically generates the extension's + * `manifest.json` asset. It reads the template, resolves the + * current version from `package.json`, and builds `content_scripts` + * entries by matching script definitions from `scripts.json` against + * page URL patterns. Shared modules, injected scripts, and + * web-accessible resources are added automatically based on import + * counts collected during the build. + */ class CreateManifestPlugin { + /** + * @param {{ + * sharedModulesImportedCount: Record<string, number>, + * injectedSharedModulesImportedCount: Record<string, number>, + * sortedSharedModules: string[] + * }} options + */ constructor({ sharedModulesImportedCount, injectedSharedModulesImportedCount, sortedSharedModules }) { this.sharedModulesImportedCount = sharedModulesImportedCount; this.injectedSharedModulesImportedCount = injectedSharedModulesImportedCount; this.sortedSharedModules = sortedSharedModules; } + /** + * @param {import("webpack").Compiler} compiler + * @returns {void} + */ apply(compiler) { - compiler.hooks.compilation.tap("CreateManifestPlugin", compilation => { + compiler.hooks.compilation.tap("CreateManifestPlugin", (compilation) => { compilation.hooks.processAssets.tap( { name: "CreateManifestPlugin", stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL }, - assets => { + (assets) => { manifest.version = JSON.parse(fs.readFileSync((path.resolve(ROOT_DIR, "package.json")))).version; manifest.content_scripts = []; let resources = []; @@ -50,12 +72,12 @@ class CreateManifestPlugin { } manifest.content_scripts.push({ - matches: [`*://banglarbhumi.gov.in/BanglarBhumi/*`], + matches: ["*://banglarbhumi.gov.in/BanglarBhumi/*"], js: this.sortedSharedModules - .filter(sharedModule => this.sharedModulesImportedCount[sharedModule] > 0) - .map(sharedModule => `shared/${getFileName(sharedModule, "shared")}.js`) + .filter((sharedModule) => this.sharedModulesImportedCount[sharedModule] > 0) + .map((sharedModule) => `shared/${getFileName(sharedModule, "shared")}.js`) .concat(arrangedScripts["*"]["document_start"]) - .filter(x => x != null), + .filter((x) => x != null), run_at: "document_start" }); @@ -83,8 +105,8 @@ class CreateManifestPlugin { } const injectedSharedModules = this.sortedSharedModules - .filter(sharedModule => this.injectedSharedModulesImportedCount[sharedModule] > 0) - .map(sharedModule => `shared/${getFileName(sharedModule, "shared")}.js`); + .filter((sharedModule) => this.injectedSharedModulesImportedCount[sharedModule] > 0) + .map((sharedModule) => `shared/${getFileName(sharedModule, "shared")}.js`); if (injectedSharedModules.length > 0) { resources = resources.concat(injectedSharedModules); diff --git a/config/webpack/plugins/create-rules-webpack-plugin.js b/config/webpack/plugins/create-rules-webpack-plugin.js index 5573ddf..ce2fbef 100644 --- a/config/webpack/plugins/create-rules-webpack-plugin.js +++ b/config/webpack/plugins/create-rules-webpack-plugin.js @@ -1,16 +1,26 @@ -import path from "path"; import fs from "fs"; +import path from "path"; + import webpack from "webpack"; +/** + * Webpack plugin that reads all JSON rule files from `src/rules/`, + * assigns sequential IDs to each rule, and emits a single `rules.json` + * asset used by `declarativeNetRequest`. + */ class CreateRulesPlugin { + /** + * @param {import("webpack").Compiler} compiler + * @returns {void} + */ apply(compiler) { - compiler.hooks.compilation.tap("CreateRulesPlugin", compilation => { + compiler.hooks.compilation.tap("CreateRulesPlugin", (compilation) => { compilation.hooks.processAssets.tap( { name: "CreateRulesPlugin", stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL }, - assets => { + (_assets) => { const ruleFileNames = fs.readdirSync(path.resolve(SOURCE_DIR, "rules")); const rules = []; let ruleId = 1; diff --git a/config/webpack/plugins/inject-script-webpack-plugin.js b/config/webpack/plugins/inject-script-webpack-plugin.js index 2b60022..16b2e98 100644 --- a/config/webpack/plugins/inject-script-webpack-plugin.js +++ b/config/webpack/plugins/inject-script-webpack-plugin.js @@ -1,29 +1,42 @@ -import webpack from "webpack"; import path from "path"; +import webpack from "webpack"; + import { getFileName } from "../utils/build-file.js"; import { getInjectedCode } from "../utils/injected-code.js"; +/** + * Webpack plugin that post-processes every `.js` asset to replace + * extension-asset URL placeholders with runtime lookup code, and + * emits an additional "injected" script that the content script + * injects into the page via the script-injector module. + */ class InjectScriptPlugin { + /** + * @param {import("webpack").Compiler} compiler + * @returns {void} + */ apply(compiler) { - compiler.hooks.compilation.tap("InjectScriptPlugin", compilation => { + compiler.hooks.compilation.tap("InjectScriptPlugin", (compilation) => { compilation.hooks.processAssets.tapPromise( { name: "InjectScriptPlugin", stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL, additionalAssets: true }, - async assets => { + async(assets) => { for (const assetName in assets) { if (/\.js$/.test(assetName)) { - const injectedCodeResponse = getInjectedCode(compilation.getAsset(assetName).source.source()) + const assetSource = compilation.getAsset(assetName).source.source(); + const injectedCodeResponse = getInjectedCode(assetSource); const scriptInjectorModuleName = getFileName("script-injector", "shared", true); compilation.updateAsset( assetName, new webpack.sources.RawSource(injectedCodeResponse[0]) ); + const injectedFileName = `scripts/injected/${path.basename(assetName)}`; assets["scripts/" + path.basename(assetName)] = new webpack.sources.RawSource( - `$${scriptInjectorModuleName}.injectScriptHead("scripts/injected/${path.basename(assetName)}", ${injectedCodeResponse[1]});` + `$${scriptInjectorModuleName}.injectScriptHead("${injectedFileName}", ${injectedCodeResponse[1]});` ); } } diff --git a/config/webpack/utils/build-file.js b/config/webpack/utils/build-file.js index 6945911..e09747d 100644 --- a/config/webpack/utils/build-file.js +++ b/config/webpack/utils/build-file.js @@ -1,6 +1,16 @@ import crypto from "crypto"; -export const getFileName = (fileName, prefix, justHash = false) => { +/** + * Generates a deterministic output filename for a module. + * In production the result is a short MD5 hash; in development the + * original name is preserved alongside the hash for easier debugging. + * + * @param {string} fileName - The original module filename. + * @param {string} prefix - A namespace prefix (e.g. "shared", "injected"). + * @param {boolean} justHash - When true, always return only the hash part. + * @returns {string} The transformed filename. + */ +export function getFileName(fileName, prefix, justHash = false) { let hash = crypto .createHash("md5") .update(`${prefix}-${fileName}`) diff --git a/config/webpack/utils/injected-code.js b/config/webpack/utils/injected-code.js index a2884f6..f2e51c8 100644 --- a/config/webpack/utils/injected-code.js +++ b/config/webpack/utils/injected-code.js @@ -1,13 +1,29 @@ import crypto from "crypto"; -const getHash = url => +/** + * Computes an MD5 hash suffix (last 16 hex characters) for a given URL. + * Used to create unique attribute names for injected script data. + * + * @param {string} url - The URL to hash. + * @returns {string} A 16-character hex hash. + */ +const getHash = (url) => crypto .createHash("md5") .update(url) .digest("hex") .substring(16); -export const getInjectedCode = code => { +/** + * Transforms extension asset URL placeholders (`"$l{ url }l$"`) in the + * compiled JavaScript into `document.currentScript.getAttribute("data-<hash>")` + * lookups. The mapping of hashes to resolved `chrome.runtime.getURL()` calls + * is returned separately so the injector script can embed it as data attributes. + * + * @param {string} code - The compiled JavaScript bundle. + * @returns {[string, string]} A tuple of [transformedCode, extensionAssetsJSON]. + */ +export function getInjectedCode(code) { const extensionAssets = {}; let i = 0; @@ -34,7 +50,7 @@ export const getInjectedCode = code => { } const extensionAssetsCode = Object.entries(extensionAssets).map( - asset => `"${asset[0]}": chrome.runtime.getURL("${asset[1]}")` + (asset) => `"${asset[0]}": chrome.runtime.getURL("${asset[1]}")` ).join(","); return [code, `{${extensionAssetsCode}}`]; diff --git a/config/webpack/utils/inline-javascript.js b/config/webpack/utils/inline-javascript.js index 456dc11..501f852 100644 --- a/config/webpack/utils/inline-javascript.js +++ b/config/webpack/utils/inline-javascript.js @@ -1 +1,10 @@ -export const inlineJavascript = code => `data:text/javascript;base64,${Buffer.from(code).toString("base64")}`; +/** + * Wraps JavaScript source code in a base64-encoded data URI so it can be + * used as an inline webpack entry point without writing a physical file. + * + * @param {string} code - The JavaScript source to inline. + * @returns {string} A `data:text/javascript;base64,…` URI. + */ +export function inlineJavascript(code) { + return `data:text/javascript;base64,${Buffer.from(code).toString("base64")}`; +} diff --git a/config/webpack/utils/script-runtime.js b/config/webpack/utils/script-runtime.js index a7c02f7..2745d54 100644 --- a/config/webpack/utils/script-runtime.js +++ b/config/webpack/utils/script-runtime.js @@ -1,4 +1,15 @@ -export const getScriptRuntimeFromType = scriptType => { +/** + * Maps a human-readable script type to a Chrome content-script + * `run_at` value. + * + * - `"before"`, `"injected-after"`, `"injected-before"` → `document_start` + * - `"rendered"` → `document_end` + * - `"loaded"` (or unknown) → `document_idle` + * + * @param {string} scriptType - The script type from scripts.json. + * @returns {string} The corresponding `run_at` value. + */ +export function getScriptRuntimeFromType(scriptType) { switch (scriptType) { case "before": case "injected-after": diff --git a/config/webpack/utils/scripts.js b/config/webpack/utils/scripts.js index af58e0a..d5a0cdc 100644 --- a/config/webpack/utils/scripts.js +++ b/config/webpack/utils/scripts.js @@ -1,10 +1,18 @@ -import path from "path"; import fs from "fs"; +import path from "path"; +/** + * Reads `src/scripts.json` and re-indexes it so the outer key is the + * URL path fragment and the inner key is the script type. This makes it + * trivial to look up which scripts run on which page. + * + * @returns {Record<string, Record<string, string[]>>} e.g. + * `{ "*": { "injected": ["stop-blocking.ts"] }, "MuteApplication.action": … }` + */ const scripts = JSON.parse( fs.readFileSync( - path.resolve("./src/scripts.json"), - ), + path.resolve("./src/scripts.json") + ) ); const formattedScripts = {}; diff --git a/config/webpack/utils/shared-modules.js b/config/webpack/utils/shared-modules.js index 2b16fa5..c9c9618 100644 --- a/config/webpack/utils/shared-modules.js +++ b/config/webpack/utils/shared-modules.js @@ -1,6 +1,12 @@ -import path from "path"; import fs from "fs"; +import path from "path"; +/** + * Scans `src/shared/` and returns the list of shared module filenames + * (with the `.ts` extension stripped), excluding `import-shared.js`. + * + * @returns {string[]} e.g. `["generate-web-page", "intercept-jquery-ajax", …]` + */ export default fs.readdirSync(path.resolve("src/shared")) - .filter(sharedModule => !sharedModule.endsWith("import-shared.js")) - .map(sharedModule => sharedModule.substring(0, sharedModule.length - 3)); + .filter((sharedModule) => !sharedModule.endsWith("import-shared.js")) + .map((sharedModule) => sharedModule.substring(0, sharedModule.length - 3)); |
