diff options
Diffstat (limited to 'uAssets/tools/make-easylist.mjs')
-rw-r--r-- | uAssets/tools/make-easylist.mjs | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/uAssets/tools/make-easylist.mjs b/uAssets/tools/make-easylist.mjs new file mode 100644 index 0000000..b648363 --- /dev/null +++ b/uAssets/tools/make-easylist.mjs @@ -0,0 +1,201 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2022-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +// jshint node:true, esversion:9 + +'use strict'; + +/******************************************************************************/ + +import fs from 'fs/promises'; +import path from 'path'; +import process from 'process'; + +/******************************************************************************/ + +const expandedParts = new Set(); + +/******************************************************************************/ + +const commandLineArgs = (( ) => { + const args = new Map(); + let name, value; + for ( const arg of process.argv.slice(2) ) { + const pos = arg.indexOf('='); + if ( pos === -1 ) { + name = arg; + value = ''; + } else { + name = arg.slice(0, pos); + value = arg.slice(pos+1); + } + args.set(name, value); + } + return args; +})(); + +/******************************************************************************/ + +function expandTemplate(wd, parts) { + const out = []; + const reInclude = /^%include +(.+):(.+)%\s+/gm; + const trim = text => trimSublist(text); + for ( const part of parts ) { + if ( typeof part !== 'string' ) { + out.push(part); + continue; + } + let lastIndex = 0; + for (;;) { + const match = reInclude.exec(part); + if ( match === null ) { break; } + out.push(part.slice(lastIndex, match.index).trim()); + const repo = match[1].trim(); + const fpath = `${match[2].trim()}`; + if ( expandedParts.has(fpath) === false ) { + console.info(` Inserting ${fpath}`); + out.push( + out.push({ file: `${fpath}` }), + `! *** ${repo}:${fpath} ***`, + fs.readFile(`${wd}/${fpath}`, { encoding: 'utf8' }) + .then(text => fpath.includes('header') ? text : trim(text)), + ); + expandedParts.add(fpath); + } + lastIndex = reInclude.lastIndex; + } + out.push(part.slice(lastIndex).trim()); + } + return out; +} + +/******************************************************************************/ + +function expandIncludeDirectives(wd, parts) { + const out = []; + const reInclude = /^!#include (.+)\s*/gm; + const trim = text => trimSublist(text); + let parentPath = ''; + for ( const part of parts ) { + if ( typeof part !== 'string' ) { + if ( typeof part === 'object' && part.file !== undefined ) { + parentPath = part.file; + } + out.push(part); + continue; + } + let lastIndex = 0; + for (;;) { + const match = reInclude.exec(part); + if ( match === null ) { break; } + out.push(part.slice(lastIndex, match.index).trim()); + const fpath = `${path.dirname(parentPath)}/${match[1].trim()}`; + if ( expandedParts.has(fpath) === false ) { + console.info(` Inserting ${fpath}`); + out.push( + { file: fpath }, + `! *** ${fpath} ***`, + fs.readFile(`${wd}/${fpath}`, { encoding: 'utf8' }) + .then(text => fpath.includes('header') ? text : trim(text)), + ); + expandedParts.add(fpath); + } + lastIndex = reInclude.lastIndex; + } + out.push(part.slice(lastIndex).trim()); + } + return out; +} + +/******************************************************************************/ + +function trimSublist(text) { + // Remove empty comment lines + text = text.replace(/^!\s*$(?:\r\n|\n)/gm, ''); + // Remove sublist header information: the importing list will provide its + // own header. + text = text.trim().replace(/^(?:!\s+[^\r\n]+?(?:\r\n|\n))+/s, ''); + return text; +} + +/******************************************************************************/ + +function minify(text) { + // remove issue-related comments + text = text.replace(/^! https:\/\/.*?[\n\r]+/gm, ''); + // remove empty lines + text = text.replace(/^[\n\r]+/gm, ''); + // convert potentially present Windows-style newlines + text = text.replace(/\r\n/g, '\n'); + return text; +} + +/******************************************************************************/ + +function assemble(parts) { + const out = []; + for ( const part of parts ) { + if ( typeof part !== 'string' ) { continue; } + out.push(part); + } + return out.join('\n').trim() + '\n'; +} + +/******************************************************************************/ + +async function main() { + const workingDir = commandLineArgs.get('dir') || '.'; + const inFile = commandLineArgs.get('in'); + if ( typeof inFile !== 'string' || inFile === '' ) { + process.exit(1); + } + const outFile = commandLineArgs.get('out'); + if ( typeof outFile !== 'string' || outFile === '' ) { + process.exit(1); + } + + console.info(` Using template at ${inFile}`); + + const inText = fs.readFile(`${workingDir}/${inFile}`, { encoding: 'utf8' }); + + let parts = [ inText ]; + do { + parts = await Promise.all(parts); + parts = expandTemplate(workingDir, parts); + } while ( parts.some(v => v instanceof Promise) ); + + do { + parts = await Promise.all(parts); + parts = expandIncludeDirectives(workingDir, parts); + } while ( parts.some(v => v instanceof Promise)); + + let afterText = assemble(parts); + + if ( commandLineArgs.get('minify') !== undefined ) { + afterText = minify(afterText); + } + + console.info(` Creating ${outFile}`); + + fs.writeFile(outFile, afterText); +} + +main(); |