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`. * * If `src/rules/` is missing or contains no JSON rule files, no asset is * emitted and a build warning is raised. `CreateManifestPlugin` relies on * that: it drops the `declarative_net_request` manifest key when * `rules.json` is absent, so the extension never ships a dangling ruleset * reference. */ class CreateRulesPlugin { /** * @param {import("webpack").Compiler} compiler * @returns {void} */ apply(compiler) { compiler.hooks.compilation.tap("CreateRulesPlugin", (compilation) => { compilation.hooks.processAssets.tap( { name: "CreateRulesPlugin", stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL }, (_assets) => { const rulesDirectoryPath = path.resolve(SOURCE_DIR, "rules"); const ruleFileNames = fs.existsSync(rulesDirectoryPath) ? fs.readdirSync(rulesDirectoryPath).filter((ruleFileName) => ruleFileName.endsWith(".json")) : []; const rules = []; let ruleId = 1; if (ruleFileNames.length === 0) { compilation.warnings.push( new Error( `No *.json rule files found in ${rulesDirectoryPath}; skipping the rules.json asset and the declarative_net_request manifest key.` ) ); return; } for (const ruleFileName of ruleFileNames) { const rule = JSON.parse(fs.readFileSync(path.resolve(rulesDirectoryPath, ruleFileName))); if (rule instanceof Array) { for (const currentRule of rule) { currentRule.id = ruleId++; rules.push(currentRule); } } else { rule.id = ruleId++; rules.push(rule); } } compilation.emitAsset( "rules.json", new webpack.sources.RawSource( JSON.stringify( rules, undefined, process.env.NODE_ENV === "production" ? undefined : "\t" ) ) ); } ); }); } } export default CreateRulesPlugin;