UNPKG

@herbertgao/surgio

Version:

Generating rules for Surge, Clash, Quantumult like a PRO

208 lines 8.24 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.loadRemoteSnippetList = exports.renderSurgioSnippet = exports.addProxyToSurgeRuleSet = exports.parseMacro = void 0; const bluebird_1 = __importDefault(require("bluebird")); const logger_1 = require("@surgio/logger"); const detect_newline_1 = __importDefault(require("detect-newline")); const ms_1 = __importDefault(require("ms")); const nunjucks_1 = __importDefault(require("nunjucks")); const babelParser = __importStar(require("@babel/parser")); const constant_1 = require("../constant"); const cache_1 = require("./cache"); const env_flag_1 = require("./env-flag"); const http_client_1 = __importDefault(require("./http-client")); const tmp_helper_1 = require("./tmp-helper"); const index_1 = require("./index"); const parseMacro = (snippet) => { const regex = /{%\s?macro(.*)\s?%}/gm; const match = regex.exec(snippet); if (!match) { throw new Error('该片段不包含可用的宏'); } const ast = babelParser.parse(match[1], {}); let statement; if (ast.errors?.length) { throw new Error('该片段不包含可用的宏'); } for (const node of ast.program.body) { if (node.type === 'ExpressionStatement') { statement = node; break; } } if (statement && statement.expression.type === 'CallExpression' && 'name' in statement.expression.callee && statement.expression.callee.name === 'main') { return { functionName: statement.expression.callee.name, arguments: statement.expression.arguments.map((item) => { if (item.type === 'Identifier') { return item.name; } else { throw new Error('该片段不包含可用的宏'); } }), }; } throw new Error('该片段不包含可用的宏'); }; exports.parseMacro = parseMacro; const addProxyToSurgeRuleSet = (str, proxyName) => { if (!proxyName) { throw new Error('必须为片段指定一个策略'); } const eol = (0, detect_newline_1.default)(str) || '\n'; return str .split(eol) .map((item) => { if (item.trim() === '' || item.startsWith('#') || item.startsWith('//')) { return item; } const rule = item.split(','); switch (rule[0].trim().toUpperCase()) { case 'URL-REGEX': case 'AND': case 'OR': case 'NOT': return `${item},${proxyName}`; case 'IP-CIDR': case 'IP-CIDR6': case 'IP-ASN': case 'GEOIP': rule.splice(2, 0, proxyName); return rule.join(','); default: if (rule[rule.length - 1].includes('#') || rule[rule.length - 1].includes('//')) { rule[rule.length - 1] = rule[rule.length - 1] .replace(/(#|\/\/)(.*)/, '') .trim(); } return [...rule, proxyName].join(','); } }) .join(eol); }; exports.addProxyToSurgeRuleSet = addProxyToSurgeRuleSet; const renderSurgioSnippet = (str, args) => { const macro = (0, exports.parseMacro)(str); macro.arguments.forEach((key, index) => { if (typeof args[index] === 'undefined') { throw new Error('Surgio 片段参数不足,缺少 ' + key); } else if (typeof args[index] !== 'string') { throw new Error(`Surgio 片段参数 ${key} 不为字符串`); } }); const template = [ `${str}`, `{{ main(${args.map((item) => JSON.stringify(item)).join(',')}) }}`, ].join('\n'); return nunjucks_1.default.renderString(template, {}).trim(); }; exports.renderSurgioSnippet = renderSurgioSnippet; const loadRemoteSnippetList = async (remoteSnippetList, cacheSnippet = true) => { const cacheType = await cache_1.unifiedCache.getType(); const useLocalFile = cacheSnippet && cacheType === 'default'; function load(url) { return http_client_1.default .get(url) .then((data) => { logger_1.logger.info(`远程片段下载成功: ${url}`); return data.body; }) .catch((err) => { logger_1.logger.error(`远程片段下载失败: ${url}`); throw err; }); } return bluebird_1.default.map(remoteSnippetList, async (item) => { const fileMd5 = (0, index_1.toMD5)(item.url); const isSurgioSnippet = item.surgioSnippet; if (useLocalFile) { const tmpFactory = (0, tmp_helper_1.createTmpFactory)(constant_1.CACHE_KEYS.RemoteSnippets, 'default'); const tmp = tmpFactory(fileMd5, (0, env_flag_1.getRemoteSnippetCacheMaxage)()); const tmpContent = await tmp.getContent(); let snippet; if (tmpContent) { snippet = tmpContent; } else { snippet = await load(item.url); await tmp.setContent(snippet); } return { main: (...args) => isSurgioSnippet ? (0, exports.renderSurgioSnippet)(snippet, args) : (0, exports.addProxyToSurgeRuleSet)(snippet, args[0]), name: item.name, url: item.url, text: snippet, // 原始内容 }; } else { const cacheKey = `${constant_1.CACHE_KEYS.RemoteSnippets}:${fileMd5}`; const cachedSnippet = await cache_1.unifiedCache.get(cacheKey); const snippet = cachedSnippet ? cachedSnippet : await load(item.url) .then((res) => { return Promise.all([ cache_1.unifiedCache.set(cacheKey, res, cacheSnippet ? (0, env_flag_1.getRemoteSnippetCacheMaxage)() : (0, ms_1.default)('1m')), res, ]); }) .then(([, res]) => res); return { main: (...args) => isSurgioSnippet ? (0, exports.renderSurgioSnippet)(snippet, args) : (0, exports.addProxyToSurgeRuleSet)(snippet, args[0]), name: item.name, url: item.url, text: snippet, // 原始内容 }; } }, { concurrency: (0, env_flag_1.getNetworkConcurrency)(), }); }; exports.loadRemoteSnippetList = loadRemoteSnippetList; //# sourceMappingURL=remote-snippet.js.map