mirror of
https://github.com/QuiteAFancyEmerald/Holy-Unblocker.git
synced 2025-05-12 11:30:01 -04:00
189 lines
8.6 KiB
JavaScript
189 lines
8.6 KiB
JavaScript
// -------------------------------------------------------------
|
|
// WARNING: this file is used by both the client and the server.
|
|
// Do not use any browser or node-specific API!
|
|
// -------------------------------------------------------------
|
|
const { parse } = require('acorn-hammerhead');
|
|
const { generate } = require('./esotope');
|
|
|
|
class JSRewriter {
|
|
constructor(ctx) {
|
|
this.parseOptions = {
|
|
allowReturnOutsideFunction: true,
|
|
allowImportExportEverywhere: true,
|
|
ecmaVersion: 2021,
|
|
};
|
|
this.generationOptions = {
|
|
format: {
|
|
quotes: 'double',
|
|
escapeless: true,
|
|
compact: true,
|
|
},
|
|
};
|
|
this.rewrite = ['location', 'parent', 'top'];
|
|
this.map = [
|
|
{
|
|
type: 'MemberExpression',
|
|
handler: (node, parent) => {
|
|
let rewrite = false;
|
|
if (parent.type == 'UnaryExpression' && parent.operator == 'delete') return;
|
|
if (parent.type == 'NewExpression' && parent.callee == node) return;
|
|
if (parent.type === 'CallExpression' && parent.callee === node) return;
|
|
if (node.preventRewrite) return;
|
|
switch(node.property.type) {
|
|
case 'Identifier':
|
|
//if (node.computed) rewrite = true;
|
|
if (!node.computed && this.rewrite.includes(node.property.name)) {
|
|
node.property = this.createLiteral(node.property.name);
|
|
rewrite = true;
|
|
};
|
|
break;
|
|
case 'Literal':
|
|
if (this.rewrite.includes(node.property.name)) rewrite = true;
|
|
break;
|
|
case 'TemplateLiteral':
|
|
rewrite = true;
|
|
break;
|
|
default:
|
|
if (node.computed) rewrite = true;
|
|
};
|
|
if (rewrite) {
|
|
let identifier = '$corrosionGet$m';
|
|
let nodeToRewrite = node;
|
|
const args = [
|
|
node.object,
|
|
node.property,
|
|
];
|
|
if (node.computed) args[1].preventRewrite = true;
|
|
if (parent.type == 'AssignmentExpression' && parent.left == node) {
|
|
identifier = '$corrosionSet$m';
|
|
nodeToRewrite = parent;
|
|
args.push(parent.right, this.createLiteral(parent.operator));
|
|
};
|
|
if (parent.type == 'CallExpression' && parent.callee == node) {
|
|
identifier = '$corrosionCall$m';
|
|
nodeToRewrite = parent;
|
|
args.push(this.createArrayExpression(...parent.arguments))
|
|
};
|
|
if (parent.type == 'UpdateExpression') {
|
|
identifier = '$corrosionSet$m';
|
|
nodeToRewrite = parent;
|
|
args.push(this.createLiteral(null), this.createLiteral(parent.operator));
|
|
};
|
|
Object.assign(nodeToRewrite, this.createCallExpression({ type: 'Identifier', name: identifier, }, args));
|
|
};
|
|
},
|
|
},
|
|
{
|
|
type: 'Identifier',
|
|
handler: (node, parent) => {
|
|
if (parent.type == 'MemberExpression' && parent.property == node) return; // window.location;
|
|
if (parent.type == 'LabeledStatement') return; // { location: null, };
|
|
if (parent.type == 'VariableDeclarator' && parent.id == node) return;
|
|
if (parent.type == 'Property' && parent.key == node) return;
|
|
if (parent.type == 'MethodDefinition') return;
|
|
if (parent.type == 'ClassDeclaration') return;
|
|
if (parent.type == 'RestElement') return;
|
|
if (parent.type == 'ExportSpecifier') return;
|
|
if (parent.type == 'ImportSpecifier') return;
|
|
if ((parent.type == 'FunctionDeclaration' || parent.type == 'FunctionExpression' || parent.type == 'ArrowFunctionExpression') && parent.params.includes(node)) return;
|
|
if ((parent.type == 'FunctionDeclaration' || parent.type == 'FunctionExpression') && parent.id == node) return;
|
|
if (parent.type == 'AssignmentPattern' && parent.left == node) return;
|
|
if (!this.rewrite.includes(node.name)) return;
|
|
if (node.preventRewrite) return;
|
|
let identifier = '$corrosionGet$';
|
|
let nodeToRewrite = node;
|
|
const args = [
|
|
this.createIdentifier(node.name, true),
|
|
];
|
|
|
|
if (parent.type == 'AssignmentExpression' && parent.left == node) {
|
|
identifier = '$corrosionSet$';
|
|
nodeToRewrite = parent;
|
|
args.push(parent.right);
|
|
args.push(this.createLiteral(parent.operator));
|
|
};
|
|
|
|
Object.assign(nodeToRewrite, this.createCallExpression({ type: 'Identifier', name: identifier }, args));
|
|
},
|
|
},
|
|
{
|
|
type: 'ImportDeclaration',
|
|
handler: (node, parent, url) => {
|
|
if (node.source.type != 'Literal' || !url) return;
|
|
node.source = this.createLiteral(ctx.url.wrap(node.source.value, { base: url, }));
|
|
},
|
|
},
|
|
{
|
|
type: 'ImportExpression',
|
|
handler: (node, parent) => {
|
|
node.source = this.createCallExpression(this.createMemberExpression(this.createMemberExpression(this.createIdentifier('$corrosion'), this.createIdentifier('url')), this.createIdentifier('wrap')), [
|
|
node.source,
|
|
this.createMemberExpression(this.createIdentifier('$corrosion'), this.createIdentifier('meta')),
|
|
]);
|
|
},
|
|
},
|
|
];
|
|
this.ctx = ctx;
|
|
};
|
|
process(source, url) {
|
|
try {
|
|
const ast = parse(source, this.parseOptions);
|
|
this.iterate(ast, (node, parent) => {
|
|
const fn = this.map.find(entry => entry.type == (node || {}).type);
|
|
if (fn) fn.handler(node, parent, url);
|
|
});
|
|
return (url ? this.createHead(url) : '') + generate(ast, this.generationOptions);
|
|
} catch(e) {
|
|
return source;
|
|
};
|
|
};
|
|
createHead(url) {
|
|
return `
|
|
if (!self.$corrosion && self.importScripts) {
|
|
importScripts(location.origin + '${this.ctx.prefix}index.js');
|
|
self.$corrosion = new Corrosion({ url: '${url}', codec: '${this.ctx.config.codec || 'plain'}', serviceWorker: true, window: self, prefix: '${this.ctx.prefix || '/service/'}', ws: ${this.ctx.config.ws || true}, cookies: ${this.ctx.config.cookies || false}, title: '${this.ctx.config.title}', }); $corrosion.init();
|
|
};\n`;
|
|
};
|
|
iterate(ast, handler) {
|
|
if (typeof ast != 'object' || !handler) return;
|
|
walk(ast, null, handler);
|
|
function walk(node, parent, handler) {
|
|
if (typeof node != 'object' || !handler) return;
|
|
handler(node, parent, handler);
|
|
for (const child in node) {
|
|
if (Array.isArray(node[child])) {
|
|
node[child].forEach(entry => walk(entry, node, handler));
|
|
} else {
|
|
walk(node[child], node, handler);
|
|
};
|
|
};
|
|
};
|
|
};
|
|
createCallExpression(callee, args) {
|
|
return { type: 'CallExpression', callee, arguments: args, optional: false, };
|
|
};
|
|
createArrayExpression(...elements) {
|
|
return {
|
|
type: 'ArrayExpression',
|
|
elements,
|
|
};
|
|
};
|
|
createMemberExpression(object, property) {
|
|
return {
|
|
type: 'MemberExpression',
|
|
object,
|
|
property,
|
|
};
|
|
};
|
|
createLiteral(value) {
|
|
return {
|
|
type: 'Literal',
|
|
value,
|
|
}
|
|
};
|
|
createIdentifier(name, preventRewrite) {
|
|
return { type: 'Identifier', name, preventRewrite: preventRewrite || false, };
|
|
};
|
|
};
|
|
|
|
module.exports = JSRewriter;
|