diff options
| -rw-r--r-- | config/webpack.config.js | 7 | ||||
| -rw-r--r-- | config/webpack/loaders/scoped-loader.js | 3 | ||||
| -rw-r--r-- | config/webpack/plugins/create-manifest-webpack-plugin/index.js | 34 | ||||
| -rw-r--r-- | config/webpack/plugins/inject-script-webpack-plugin.js | 52 | ||||
| -rw-r--r-- | config/webpack/plugins/sanitize-scripts-import-webpack-plugin.js | 56 | ||||
| -rw-r--r-- | config/webpack/utils/generate-script-build-config.js | 37 | ||||
| -rw-r--r-- | package.json | 3 | ||||
| -rw-r--r-- | src/manifest.json | 10 | ||||
| -rw-r--r-- | src/scripts/auto-login.ts | 2 | ||||
| -rw-r--r-- | src/scripts/block-unnecessary.ts | 20 | ||||
| -rw-r--r-- | yarn.lock | 5 |
11 files changed, 168 insertions, 61 deletions
diff --git a/config/webpack.config.js b/config/webpack.config.js index f21d38f..e7ffaa7 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -2,6 +2,7 @@ const path = require("path"); const CopyPlugin = require("copy-webpack-plugin"); process.env.ROOT_DIR = path.resolve(__dirname, ".."); +process.env.CONFIG_DIR = path.resolve(process.env.ROOT_DIR, "config"); process.env.SOURCE_DIR = path.resolve(process.env.ROOT_DIR, "src"); const CreateManifestPlugin = require("./webpack/plugins/create-manifest-webpack-plugin"); @@ -12,9 +13,7 @@ const manifest = require(path.resolve(process.env.SOURCE_DIR, "manifest.json")); let scripts = []; for (const scriptType in manifest) { - for (const scriptName in manifest[scriptType]) { - scripts.push(generateScriptBuildConfig(scriptName, scriptType)); - } + scripts.push(generateScriptBuildConfig(scriptType)); } scripts[scripts.length - 1].plugins = (scripts[scripts.length - 1].plugins || []).concat([ @@ -24,7 +23,7 @@ scripts[scripts.length - 1].plugins = (scripts[scripts.length - 1].plugins || [] to: "./" }] }), - new CreateManifestPlugin(manifest) + new CreateManifestPlugin({ scripts: manifest }) ]); module.exports = scripts; diff --git a/config/webpack/loaders/scoped-loader.js b/config/webpack/loaders/scoped-loader.js new file mode 100644 index 0000000..701afc2 --- /dev/null +++ b/config/webpack/loaders/scoped-loader.js @@ -0,0 +1,3 @@ +module.exports = source => `(() => { +${source} +})();`; diff --git a/config/webpack/plugins/create-manifest-webpack-plugin/index.js b/config/webpack/plugins/create-manifest-webpack-plugin/index.js index 5dac1b2..4a07380 100644 --- a/config/webpack/plugins/create-manifest-webpack-plugin/index.js +++ b/config/webpack/plugins/create-manifest-webpack-plugin/index.js @@ -2,17 +2,31 @@ const path = require("path"); const { version } = require(path.resolve(process.env.ROOT_DIR, "package.json")); +const getRunAt = scriptType => { + switch (scriptType) { + case "before": + case "injected": + case "injected_and_loaded": + return "document_start"; + + case "rendered": + return "document_end"; + + case "loaded": + default: + return "document_idle"; + } +}; + class CreateManifestPlugin { - constructor(scripts) { + constructor({ scripts }) { this.scripts = {}; this.manifest = require("./manifest-template.json"); - this.scripts.document_start = { - ...(scripts.before || {}), - ...(scripts.injected || {}), - ...(scripts.injected_and_loaded || {}) - }; - this.scripts.document_end = scripts.rendered || {}; - this.scripts.document_idle = scripts.loaded || {}; + this.scripts.before = scripts.before || {}; + this.scripts.injected = scripts.injected || {}; + this.scripts.injected_and_loaded = scripts.injected_and_loaded || {}; + this.scripts.rendered = scripts.rendered || {}; + this.scripts.loaded = scripts.loaded || {}; } apply(compiler) { @@ -43,8 +57,8 @@ class CreateManifestPlugin { for (const scriptPath in paths) { this.manifest.content_scripts.push({ matches: [`*://banglarbhumi.gov.in/${scriptPath}`], - js: paths[scriptPath].map(scriptName => `${scriptName}.js`), - run_at: scriptType + js: [`${scriptType}.js`], + run_at: getRunAt(scriptType) }); } } diff --git a/config/webpack/plugins/inject-script-webpack-plugin.js b/config/webpack/plugins/inject-script-webpack-plugin.js index ca75213..6568514 100644 --- a/config/webpack/plugins/inject-script-webpack-plugin.js +++ b/config/webpack/plugins/inject-script-webpack-plugin.js @@ -1,5 +1,23 @@ +const { sources } = require("webpack"); const terser = require("terser"); +const propectualIndexOf = (string, characterList, start) => { + if (start > 0) { + let i = 0; + let x = start; + while (i < characterList.length) { + x = string.indexOf(characterList[i], x + 1); + if (x < 0) { + return -1; + } + i++; + } + return x; + } else { + return -1; + } +}; + class InjectScriptPlugin { constructor({ onload, isFirst }) { this.onload = !!onload; @@ -11,45 +29,39 @@ class InjectScriptPlugin { compilation.hooks.processAssets.tapPromise( { name: "InjectScriptPlugin", - stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE, + stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE, additionalAssets: true }, async assets => { for (const assetName in assets) { if (assetName.endsWith(".js")) { - const asset = assets[assetName]; - let content = asset._source._children; - let x = -1; - for (let i = 0; i < content.length; i++) { - if (typeof content[i] === "string" && content[i].indexOf("__webpack_exports__") >= 0) { - x = i; - break; - } - } - if (x == -1) { - throw Error("Failed to inject script."); - } - content[x] += ` + let currentAssetContent = compilation.getAsset(assetName).source.source(); + let start = ` const observer = new MutationObserver(mutations => { const script = document.querySelector("head > script:nth-child(54)"); if (script) { script.innerHTML `; + let end = ""; if (!this.isFirst) { - content[x] += "+"; + start += "+"; } - content[x] += "= `"; + start += "= `"; if (this.onload) { - content[x] += "document.addEventListener(\"DOMContentLoaded\", () => {"; - content[content.length - 3] += "});"; + start += "document.addEventListener(\"DOMContentLoaded\", () => {"; + end += "});"; } - content[content.length - 3] += `\`; + end += `\`; observer.disconnect(); } }); observer.observe(document, {childList: true, subtree: true}); `; - content[x + 1] = (await terser.minify(content[x + 1]._source._source._valueAsString.replaceAll("`", "\\`"))).code; + const content = (await terser.minify(currentAssetContent.replaceAll("`", "\\`"))).code; + compilation.updateAsset( + assetName, + new sources.RawSource(start + content + end) + ); } } } diff --git a/config/webpack/plugins/sanitize-scripts-import-webpack-plugin.js b/config/webpack/plugins/sanitize-scripts-import-webpack-plugin.js new file mode 100644 index 0000000..61ce804 --- /dev/null +++ b/config/webpack/plugins/sanitize-scripts-import-webpack-plugin.js @@ -0,0 +1,56 @@ +const { sources } = require("webpack"); + +const propectualIndexOf = (string, characterList, start) => { + if (start > 0) { + let i = 0; + let x = start; + while (i < characterList.length) { + x = string.indexOf(characterList[i], x + 1); + if (x < 0) { + return -1; + } + i++; + } + return x; + } else { + return -1; + } +}; + +class SanitizeScriptsImportPlugin { + apply(compiler) { + compiler.hooks.compilation.tap("SanitizeScriptsImportPlugin", compilation => { + compilation.hooks.processAssets.tap( + { + name: "SanitizeScriptsImportPlugin", + stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE, + additionalAssets: true + }, + assets => { + for (const assetName in assets) { + if (assetName.endsWith(".js")) { + const currentAssetContent = compilation.getAsset(assetName).source.source(); + let previouslyEnded = 0; + let code = ""; + while (true) { + const start = propectualIndexOf(currentAssetContent, ["\n/***/", "\n/***/", "\n"], previouslyEnded + 1); + previouslyEnded = propectualIndexOf(currentAssetContent, ["\n/***/"], start + 1); + if (previouslyEnded < 0) { + break; + } else { + code += currentAssetContent.substring(start, previouslyEnded); + } + } + compilation.updateAsset( + assetName, + new sources.RawSource(code) + ); + } + } + } + ); + }); + } +} + +module.exports = SanitizeScriptsImportPlugin; diff --git a/config/webpack/utils/generate-script-build-config.js b/config/webpack/utils/generate-script-build-config.js index 2485e85..f8e1697 100644 --- a/config/webpack/utils/generate-script-build-config.js +++ b/config/webpack/utils/generate-script-build-config.js @@ -1,26 +1,31 @@ const fs = require("fs"); const path = require("path"); +const SanitizeScriptsImportPlugin = require("../plugins/sanitize-scripts-import-webpack-plugin"); const InjectScriptPlugin = require("../plugins/inject-script-webpack-plugin"); const manifest = require(path.resolve(process.env.SOURCE_DIR, "manifest.json")); let count = 0; -const generateScriptBuildConfig = (scriptName, scriptType) => { - let entry = path.resolve(process.env.SOURCE_DIR, `scripts/${scriptName}.ts`); - if (!fs.existsSync(entry)) { - entry = path.resolve(process.env.SOURCE_DIR, `scripts/${scriptName}/index.ts`); - if (!fs.existsSync(entry)) { - entry = path.resolve(process.env.SOURCE_DIR, `scripts/${scriptName}.js`); - } - } +const getImportStatementForScript = script => { + const importPath = path.resolve(process.env.SOURCE_DIR, "scripts", script); + return `import "${importPath}";`; +}; + +const getVirtualEntry = scriptType => { + const string = Object.keys(manifest[scriptType]).map(getImportStatementForScript).join("\n"); + const base64 = Buffer.from(string).toString("base64"); + return `data:text/javascript;base64,${base64}`; +} + +const generateScriptBuildConfig = scriptType => { return { - entry, + entry: getVirtualEntry(scriptType), mode: "production", output: { - filename: `${scriptName}.js` + filename: `${scriptType}.js` }, resolve: { extensions: ["", ".ts", ".js"], @@ -36,15 +41,23 @@ const generateScriptBuildConfig = (scriptName, scriptType) => { options: { configFile: "config/tsconfig.json" } + }, + { + test: new RegExp(`^${path.resolve(process.env.SOURCE_DIR, "scripts")}`), + loader: path.resolve(process.env.CONFIG_DIR, "webpack/loaders/scoped-loader") } ] }, - plugins: scriptType.startsWith("injected") + plugins: [ + new SanitizeScriptsImportPlugin() + ].concat( + scriptType.startsWith("injected") ? [new InjectScriptPlugin({ onload: scriptType === "injected_and_loaded", isFirst: count++ <= 0 })] - : undefined + : [] + ) }; }; diff --git a/package.json b/package.json index 2085ef6..7b0fb5f 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "ts-loader": "^9.4.2", "typescript": "^4.9.5", "webpack": "^5.75.0", - "webpack-cli": "^5.0.1" + "webpack-cli": "^5.0.1", + "webpack-virtual-modules": "^0.5.0" } } diff --git a/src/manifest.json b/src/manifest.json index 397ec6e..8e8d0bd 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,15 +1,15 @@ { "before": { - "block-unnecessary": "*" + "block-unnecessary.ts": "*" }, "injected": { - "stop-blocking": "*" + "stop-blocking.js": "*" }, "injected_and_loaded": { - "autofill-otps": "*", - "auto-login": "*" + "autofill-otps.ts": "*", + "auto-login.ts": "*" }, "loaded": { - "autofill-captcha": "*" + "autofill-captcha.ts": "*" } } diff --git a/src/scripts/auto-login.ts b/src/scripts/auto-login.ts index 0da8ce3..1d2b379 100644 --- a/src/scripts/auto-login.ts +++ b/src/scripts/auto-login.ts @@ -1,6 +1,6 @@ const proxiedPost = $.post; -let loginData: any = null; +let loginData: any | null = null; $.post = function() { if (arguments[0].endsWith("login.action")) { diff --git a/src/scripts/block-unnecessary.ts b/src/scripts/block-unnecessary.ts index 78d294b..4163778 100644 --- a/src/scripts/block-unnecessary.ts +++ b/src/scripts/block-unnecessary.ts @@ -1,19 +1,23 @@ type ConditionalString = string | undefined; -let scriptsToBlock: ConditionalString[] = [ +let blocked: ConditionalString[] = [ "head > script:nth-child(34)", "head > script:nth-child(43)" ]; let count = 0; const observer = new MutationObserver(mutations => { - for (let i = 0; i < scriptsToBlock.length; i++) { - if (scriptsToBlock[i]) { - const script = document.querySelector(scriptsToBlock[i]!) as HTMLScriptElement; - if (script) { - script.src = ""; + for (let i = 0; i < blocked.length; i++) { + if (blocked[i]) { + const element = document.querySelector(blocked[i]!) as HTMLElement | null; + if (element) { + if (element instanceof HTMLScriptElement) { + element.src = ""; + } else if (element instanceof HTMLLinkElement && element.getAttribute("rel") === "stylesheet") { + element.href = ""; + } + blocked[i] = undefined; count++; - scriptsToBlock[i] = undefined; - if (count >= scriptsToBlock.length) { + if (count >= blocked.length) { observer.disconnect(); break; } @@ -1057,6 +1057,11 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== +webpack-virtual-modules@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz#362f14738a56dae107937ab98ea7062e8bdd3b6c" + integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw== + webpack@^5.75.0: version "5.75.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.75.0.tgz#1e440468647b2505860e94c9ff3e44d5b582c152" |
