mirror of
https://github.com/SukkaW/Surge.git
synced 2025-12-12 01:00:34 +08:00
Chore: make ESLint Happy
This commit is contained in:
parent
40cda6997a
commit
e40979e50e
@ -64,7 +64,8 @@ export const buildCommon = task(require.main === module, __filename)(async (span
|
||||
|
||||
const $skip = Symbol('skip');
|
||||
|
||||
const processFile = (span: Span, sourcePath: string) => span.traceChildAsync(`process file: ${sourcePath}`, async () => {
|
||||
function processFile(span: Span, sourcePath: string) {
|
||||
return span.traceChildAsync(`process file: ${sourcePath}`, async () => {
|
||||
const lines: string[] = [];
|
||||
|
||||
let title = '';
|
||||
@ -104,6 +105,7 @@ const processFile = (span: Span, sourcePath: string) => span.traceChildAsync(`pr
|
||||
|
||||
return [title, descriptions, lines, sgmodulePathname] as const;
|
||||
});
|
||||
}
|
||||
|
||||
function transformDomainset(parentSpan: Span, sourcePath: string, relativePath: string) {
|
||||
return parentSpan
|
||||
|
||||
@ -12,7 +12,7 @@ import { appendArrayInPlace } from './lib/append-array-in-place';
|
||||
import { OUTPUT_INTERNAL_DIR, OUTPUT_MODULES_DIR, SOURCE_DIR } from './constants/dir';
|
||||
import { RulesetOutput } from './lib/create-file';
|
||||
|
||||
const getRule = (domain: string) => {
|
||||
function getRule(domain: string) {
|
||||
switch (domain[0]) {
|
||||
case '+':
|
||||
case '$':
|
||||
@ -20,7 +20,7 @@ const getRule = (domain: string) => {
|
||||
default:
|
||||
return `DOMAIN-SUFFIX,${domain}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
export const getDomesticAndDirectDomainsRulesetPromise = createMemoizedPromise(async () => {
|
||||
const domestics = await readFileIntoProcessedArray(path.join(SOURCE_DIR, 'non_ip/domestic.conf'));
|
||||
const directs = await readFileIntoProcessedArray(path.resolve(SOURCE_DIR, 'non_ip/direct.conf'));
|
||||
|
||||
@ -14,7 +14,7 @@ import { compareAndWriteFile } from './lib/create-file';
|
||||
const mockDir = path.join(ROOT_DIR, 'Mock');
|
||||
const modulesDir = path.join(ROOT_DIR, 'Modules');
|
||||
|
||||
const copyDirContents = async (srcDir: string, destDir: string) => {
|
||||
async function copyDirContents(srcDir: string, destDir: string) {
|
||||
const promises: Array<Promise<void>> = [];
|
||||
|
||||
for await (const entry of await fsp.opendir(srcDir)) {
|
||||
@ -28,7 +28,7 @@ const copyDirContents = async (srcDir: string, destDir: string) => {
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
};
|
||||
}
|
||||
|
||||
export const buildPublic = task(require.main === module, __filename)(async (span) => {
|
||||
await span.traceChildAsync('copy rest of the files', async () => {
|
||||
@ -83,7 +83,7 @@ const prioritySorter = (a: TreeType, b: TreeType) => ((priorityOrder[a.name] ||
|
||||
|
||||
const html = (string: TemplateStringsArray, ...values: any[]) => string.reduce((acc, str, i) => acc + str + (values[i] ?? ''), '');
|
||||
|
||||
const walk = (tree: TreeTypeArray) => {
|
||||
function walk(tree: TreeTypeArray) {
|
||||
let result = '';
|
||||
tree.sort(prioritySorter);
|
||||
for (let i = 0, len = tree.length; i < len; i++) {
|
||||
@ -102,7 +102,7 @@ const walk = (tree: TreeTypeArray) => {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
function generateHtml(tree: TreeTypeArray) {
|
||||
return html`
|
||||
|
||||
@ -141,7 +141,7 @@ const latestTopUserAgentsPromise = $fetch('https://cdn.jsdelivr.net/npm/top-user
|
||||
.then(res => res.json())
|
||||
.then((userAgents: string[]) => userAgents.filter(ua => ua.startsWith('Mozilla/5.0 ')));
|
||||
|
||||
const querySpeedtestApi = async (keyword: string): Promise<Array<string | null>> => {
|
||||
async function querySpeedtestApi(keyword: string): Promise<Array<string | null>> {
|
||||
const topUserAgents = await latestTopUserAgentsPromise;
|
||||
|
||||
const url = `https://www.speedtest.net/api/js/servers?engine=js&search=${keyword}&limit=100`;
|
||||
@ -181,7 +181,7 @@ const querySpeedtestApi = async (keyword: string): Promise<Array<string | null>>
|
||||
console.error(e);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const buildSpeedtestDomainSet = task(require.main === module, __filename)(async (span) => {
|
||||
const output = new DomainsetOutput(span, 'speedtest')
|
||||
|
||||
@ -6,11 +6,10 @@ import { ALL, NORTH_AMERICA, EU, HK, TW, JP, KR } from '../Source/stream';
|
||||
import { SHARED_DESCRIPTION } from './lib/constants';
|
||||
import { RulesetOutput } from './lib/create-file';
|
||||
|
||||
export const createRulesetForStreamService = (
|
||||
span: Span,
|
||||
export function createRulesetForStreamService(span: Span,
|
||||
fileId: string, title: string,
|
||||
streamServices: Array<import('../Source/stream').StreamService>
|
||||
) => span.traceChildAsync(fileId, async (childSpan) => Promise.all([
|
||||
streamServices: Array<import('../Source/stream').StreamService>) {
|
||||
return span.traceChildAsync(fileId, async (childSpan) => Promise.all([
|
||||
// Domains
|
||||
new RulesetOutput(childSpan, fileId, 'non_ip')
|
||||
.withTitle(`Sukka's Ruleset - Stream Services: ${title}`)
|
||||
@ -33,6 +32,7 @@ export const createRulesetForStreamService = (
|
||||
.bulkAddCIDR6NoResolve(streamServices.flatMap(i => i.ip?.v6 ?? []))
|
||||
.write()
|
||||
]));
|
||||
}
|
||||
|
||||
export const buildStreamService = task(require.main === module, __filename)(async (span) => Promise.all([
|
||||
createRulesetForStreamService(span, 'stream', 'All', ALL),
|
||||
|
||||
@ -6,14 +6,14 @@ type Node = Map<string, Node> & {
|
||||
[FAIL]: Node | undefined
|
||||
};
|
||||
|
||||
const createNode = (): Node => {
|
||||
function createNode(): Node {
|
||||
const node = new Map<string, Node | undefined>() as Node;
|
||||
node[WORDEND] = false;
|
||||
node[FAIL] = undefined;
|
||||
return node;
|
||||
};
|
||||
}
|
||||
|
||||
const createKeywordFilter = (keys: string[] | Set<string>) => {
|
||||
function createKeywordFilter(keys: string[] | Set<string>) {
|
||||
const root = createNode();
|
||||
|
||||
// Create a trie with extra fields and information
|
||||
@ -82,6 +82,6 @@ const createKeywordFilter = (keys: string[] | Set<string>) => {
|
||||
|
||||
return false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default createKeywordFilter;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { Writable } from 'node:stream';
|
||||
import { once } from 'node:events';
|
||||
|
||||
export const asyncWriteToStream = <T>(stream: Writable, chunk: T) => {
|
||||
export function asyncWriteToStream<T>(stream: Writable, chunk: T) {
|
||||
const res = stream.write(chunk);
|
||||
if (!res) {
|
||||
return once(stream, 'drain'); // returns a promise only if needed
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
export const pack = (a: number, b: number): number => (a << 16) | b;
|
||||
|
||||
/** Unpacks two 16-bit integers from one 32-bit integer */
|
||||
export const unpack = (value: number, arr: [a: number, b: number] = Array.from(new Array(2).keys()) as any): [a: number, b: number] => {
|
||||
export function unpack(value: number, arr: [a: number, b: number] = Array.from(new Array(2).keys()) as any): [a: number, b: number] {
|
||||
arr[0] = (value >> 16) & 0xFFFF;
|
||||
arr[1] = value & 0xFFFF;
|
||||
return arr;
|
||||
};
|
||||
}
|
||||
|
||||
export const unpackFirst = (value: number): number => (value >> 16) & 0xFFFF;
|
||||
export const unpackSecond = (value: number): number => value & 0xFFFF;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import process from 'node:process';
|
||||
|
||||
export const createCache = (namespace?: string, printStats = false) => {
|
||||
export function createCache(namespace?: string, printStats = false) {
|
||||
const cache = new Map();
|
||||
|
||||
let hit = 0;
|
||||
@ -30,4 +30,4 @@ export const createCache = (namespace?: string, printStats = false) => {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@ -410,7 +410,7 @@ export const serializeArray = (arr: string[]) => fastStringArrayJoin(arr, separa
|
||||
export const deserializeArray = (str: string) => str.split(separator);
|
||||
|
||||
export const getFileContentHash = (filename: string) => stringHash(fs.readFileSync(filename, 'utf-8'));
|
||||
export const createCacheKey = (filename: string) => {
|
||||
export function createCacheKey(filename: string) {
|
||||
const fileHash = getFileContentHash(filename);
|
||||
return (key: string) => key + '$' + fileHash + '$';
|
||||
};
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ const mihomoBinaryUrl: Partial<Record<NodeJS.Platform, Partial<Record<NodeJS.Arc
|
||||
}
|
||||
};
|
||||
|
||||
const ensureMihomoBinary = async () => {
|
||||
async function ensureMihomoBinary() {
|
||||
await mkdirp(mihomoBinaryDir);
|
||||
if (!fs.existsSync(mihomoBinaryPath)) {
|
||||
const writeStream = fs.createWriteStream(mihomoBinaryPath);
|
||||
@ -47,9 +47,9 @@ const ensureMihomoBinary = async () => {
|
||||
);
|
||||
}
|
||||
await fsp.chmod(mihomoBinaryPath, 0o755);
|
||||
};
|
||||
}
|
||||
|
||||
export const convertClashMetaMrs = async (type: 'domain', format: 'text', input: string, output: string) => {
|
||||
export async function convertClashMetaMrs(type: 'domain', format: 'text', input: string, output: string) {
|
||||
await ensureMihomoBinary();
|
||||
|
||||
const { stderr } = await ezspawn(mihomoBinaryPath, ['convert-ruleset', type, format, input, output]);
|
||||
@ -57,4 +57,4 @@ export const convertClashMetaMrs = async (type: 'domain', format: 'text', input:
|
||||
if (stderr) {
|
||||
throw new Error(stderr);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -8,9 +8,9 @@ const createSource = async function *(input: string[]) {
|
||||
}
|
||||
};
|
||||
|
||||
const test = async (a: string[], b: string[], expected: boolean) => {
|
||||
async function test(a: string[], b: string[], expected: boolean) {
|
||||
expect((await fileEqual(a, createSource(b)))).to.eq(expected);
|
||||
};
|
||||
}
|
||||
|
||||
describe('fileEqual', () => {
|
||||
it('same', () => test(
|
||||
|
||||
@ -26,7 +26,8 @@ export class CustomNoETagFallbackError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export const sleepWithAbort = (ms: number, signal: AbortSignal) => new Promise<void>((resolve, reject) => {
|
||||
export function sleepWithAbort(ms: number, signal: AbortSignal) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (signal.aborted) {
|
||||
reject(signal.reason as Error);
|
||||
return;
|
||||
@ -39,6 +40,7 @@ export const sleepWithAbort = (ms: number, signal: AbortSignal) => new Promise<v
|
||||
|
||||
function stop(this: AbortSignal) { reject(this.reason as Error); }
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchAssets(url: string, fallbackUrls: string[] | readonly string[]) {
|
||||
const controller = new AbortController();
|
||||
|
||||
@ -95,26 +95,6 @@ function createFetchRetry($fetch: typeof fetch): FetchWithRetry {
|
||||
return res;
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
const mayBailError = (err: unknown) => {
|
||||
if (typeof err === 'object' && err !== null && 'name' in err) {
|
||||
if ((
|
||||
err.name === 'AbortError'
|
||||
|| ('digest' in err && err.digest === 'AbortError')
|
||||
) && !retryOpts.retryOnAborted) {
|
||||
console.log(picocolors.gray('[fetch abort]'), url);
|
||||
return true;
|
||||
}
|
||||
if (err.name === 'Custom304NotModifiedError') {
|
||||
return true;
|
||||
}
|
||||
if (err.name === 'CustomNoETagFallbackError') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return !!(isClientError(err));
|
||||
};
|
||||
|
||||
if (mayBailError(err)) {
|
||||
return bail(err) as never;
|
||||
};
|
||||
@ -140,6 +120,26 @@ function createFetchRetry($fetch: typeof fetch): FetchWithRetry {
|
||||
throw newErr;
|
||||
}
|
||||
}, retryOpts);
|
||||
|
||||
function mayBailError(err: unknown) {
|
||||
if (typeof err === 'object' && err !== null && 'name' in err) {
|
||||
if ((
|
||||
err.name === 'AbortError'
|
||||
|| ('digest' in err && err.digest === 'AbortError')
|
||||
) && !retryOpts.retryOnAborted) {
|
||||
console.log(picocolors.gray('[fetch abort]'), url);
|
||||
return true;
|
||||
}
|
||||
if (err.name === 'Custom304NotModifiedError') {
|
||||
return true;
|
||||
}
|
||||
if (err.name === 'CustomNoETagFallbackError') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return !!(isClientError(err));
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof ResponseError) {
|
||||
return err.res;
|
||||
|
||||
@ -9,19 +9,19 @@ import { processLine } from './process-line';
|
||||
import { $fetch } from './make-fetch-happen';
|
||||
import type { NodeFetchResponse } from './make-fetch-happen';
|
||||
|
||||
const getReadableStream = (file: string | FileHandle): ReadableStream => {
|
||||
function getReadableStream(file: string | FileHandle): ReadableStream {
|
||||
if (typeof file === 'string') {
|
||||
// return fs.openAsBlob(file).then(blob => blob.stream())
|
||||
return Readable.toWeb(fs.createReadStream(file/* , { encoding: 'utf-8' } */));
|
||||
}
|
||||
return file.readableWebStream();
|
||||
};
|
||||
}
|
||||
// TODO: use FileHandle.readLine()
|
||||
export const readFileByLine: ((file: string | FileHandle) => AsyncIterable<string>) = (file: string | FileHandle) => getReadableStream(file)
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(new TextLineStream());
|
||||
|
||||
const ensureResponseBody = <T extends Response | NodeFetchResponse>(resp: T): NonNullable<T['body']> => {
|
||||
function ensureResponseBody<T extends Response | NodeFetchResponse>(resp: T): NonNullable<T['body']> {
|
||||
if (!resp.body) {
|
||||
throw new Error('Failed to fetch remote text');
|
||||
}
|
||||
@ -29,7 +29,7 @@ const ensureResponseBody = <T extends Response | NodeFetchResponse>(resp: T): No
|
||||
throw new Error('Body has already been consumed.');
|
||||
}
|
||||
return resp.body;
|
||||
};
|
||||
}
|
||||
|
||||
export const createReadlineInterfaceFromResponse: ((resp: Response | NodeFetchResponse) => AsyncIterable<string>) = (resp) => {
|
||||
const stream = ensureResponseBody(resp);
|
||||
|
||||
@ -101,7 +101,8 @@ const lowKeywords = createKeywordFilter([
|
||||
|
||||
const cacheKey = createCacheKey(__filename);
|
||||
|
||||
export const getPhishingDomains = (parentSpan: Span) => parentSpan.traceChild('get phishing domains').traceAsyncFn(async (span) => {
|
||||
export function getPhishingDomains(parentSpan: Span) {
|
||||
return parentSpan.traceChild('get phishing domains').traceAsyncFn(async (span) => {
|
||||
const domainArr = await span.traceChildAsync('download/parse/merge phishing domains', async (curSpan) => {
|
||||
const domainArr: string[] = [];
|
||||
|
||||
@ -120,6 +121,7 @@ export const getPhishingDomains = (parentSpan: Span) => parentSpan.traceChild('g
|
||||
() => processPhihsingDomains(domainArr, cacheHash)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function processPhihsingDomains(domainArr: string[], cacheHash = '') {
|
||||
return fsFetchCache.apply(
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
const notError = Symbol('notError');
|
||||
|
||||
export const createMemoizedPromise = <T>(fn: () => Promise<T>, preload = true): () => Promise<T> => {
|
||||
export function createMemoizedPromise<T>(fn: () => Promise<T>, preload = true): () => Promise<T> {
|
||||
let error: Error | typeof notError = notError;
|
||||
|
||||
let promise: Promise<T> | null = preload
|
||||
@ -19,4 +19,4 @@ export const createMemoizedPromise = <T>(fn: () => Promise<T>, preload = true):
|
||||
promise ??= fn();
|
||||
return promise;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import { OUTPUT_CLASH_DIR, OUTPUT_SINGBOX_DIR, OUTPUT_SURGE_DIR } from '../const
|
||||
|
||||
export const isTruthy = <T>(i: T | 0 | '' | false | null | undefined): i is T => !!i;
|
||||
|
||||
export const fastStringArrayJoin = (arr: string[], sep: string) => {
|
||||
export function fastStringArrayJoin(arr: string[], sep: string) {
|
||||
let result = '';
|
||||
for (let i = 0, len = arr.length; i < len; i++) {
|
||||
if (i !== 0) {
|
||||
@ -14,7 +14,7 @@ export const fastStringArrayJoin = (arr: string[], sep: string) => {
|
||||
result += arr[i];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
interface Write {
|
||||
(
|
||||
@ -23,12 +23,12 @@ interface Write {
|
||||
): Promise<unknown>
|
||||
}
|
||||
|
||||
export const mkdirp = (dir: string) => {
|
||||
export function mkdirp(dir: string) {
|
||||
if (fs.existsSync(dir)) {
|
||||
return;
|
||||
}
|
||||
return fsp.mkdir(dir, { recursive: true });
|
||||
};
|
||||
}
|
||||
|
||||
export const writeFile: Write = async (destination: string, input, dir = dirname(destination)) => {
|
||||
const p = mkdirp(dir);
|
||||
@ -40,7 +40,7 @@ export const writeFile: Write = async (destination: string, input, dir = dirname
|
||||
|
||||
export const removeFiles = async (files: string[]) => Promise.all(files.map((file) => fsp.rm(file, { force: true })));
|
||||
|
||||
export const domainWildCardToRegex = (domain: string) => {
|
||||
export function domainWildCardToRegex(domain: string) {
|
||||
let result = '^';
|
||||
for (let i = 0, len = domain.length; i < len; i++) {
|
||||
switch (domain[i]) {
|
||||
@ -59,11 +59,11 @@ export const domainWildCardToRegex = (domain: string) => {
|
||||
}
|
||||
result += '$';
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export const identity = <T>(x: T): T => x;
|
||||
|
||||
export const appendArrayFromSet = <T>(dest: T[], source: Set<T> | Array<Set<T>>, transformer: (item: T) => T = identity) => {
|
||||
export function appendArrayFromSet<T>(dest: T[], source: Set<T> | Array<Set<T>>, transformer: (item: T) => T = identity) {
|
||||
const casted = Array.isArray(source) ? source : [source];
|
||||
for (let i = 0, len = casted.length; i < len; i++) {
|
||||
const iterator = casted[i].values();
|
||||
@ -75,13 +75,15 @@ export const appendArrayFromSet = <T>(dest: T[], source: Set<T> | Array<Set<T>>,
|
||||
}
|
||||
|
||||
return dest;
|
||||
};
|
||||
}
|
||||
|
||||
export const output = (id: string, type: 'non_ip' | 'ip' | 'domainset') => [
|
||||
export function output(id: string, type: 'non_ip' | 'ip' | 'domainset') {
|
||||
return [
|
||||
path.join(OUTPUT_SURGE_DIR, type, id + '.conf'),
|
||||
path.join(OUTPUT_CLASH_DIR, type, id + '.txt'),
|
||||
path.join(OUTPUT_SINGBOX_DIR, type, id + '.json')
|
||||
] as const;
|
||||
}
|
||||
|
||||
export function withBannerArray(title: string, description: string[] | readonly string[], date: Date, content: string[]) {
|
||||
return [
|
||||
@ -96,7 +98,7 @@ export function withBannerArray(title: string, description: string[] | readonly
|
||||
];
|
||||
};
|
||||
|
||||
export const mergeHeaders = (headersA: RequestInit['headers'] | undefined, headersB: RequestInit['headers']) => {
|
||||
export function mergeHeaders(headersA: RequestInit['headers'] | undefined, headersB: RequestInit['headers']) {
|
||||
if (headersA == null) {
|
||||
return headersB;
|
||||
}
|
||||
@ -116,9 +118,9 @@ export const mergeHeaders = (headersA: RequestInit['headers'] | undefined, heade
|
||||
|
||||
for (const key in headersB) {
|
||||
if (Object.hasOwn(headersB, key)) {
|
||||
result.set(key, (headersB as Record<string, string>)[key]);
|
||||
result.set(key, (headersB)[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import { normalizeTldtsOpt } from '../constants/loose-tldts-opt';
|
||||
|
||||
type TldTsParsed = ReturnType<typeof tldts.parse>;
|
||||
|
||||
export const normalizeDomain = (domain: string, parsed: TldTsParsed | null = null) => {
|
||||
export function normalizeDomain(domain: string, parsed: TldTsParsed | null = null) {
|
||||
if (domain.length === 0) return null;
|
||||
|
||||
parsed ??= tldts.parse(domain, normalizeTldtsOpt);
|
||||
@ -29,4 +29,4 @@ export const normalizeDomain = (domain: string, parsed: TldTsParsed | null = nul
|
||||
}
|
||||
|
||||
return h.length > 0 ? h : null;
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,19 +3,19 @@ import { parse as tldtsParse } from 'tldts';
|
||||
import { $fetch } from './make-fetch-happen';
|
||||
import type { NodeFetchResponse } from './make-fetch-happen';
|
||||
|
||||
const isDomainLoose = (domain: string): boolean => {
|
||||
function isDomainLoose(domain: string): boolean {
|
||||
const { isIcann, isPrivate, isIp } = tldtsParse(domain);
|
||||
return !!(!isIp && (isIcann || isPrivate));
|
||||
};
|
||||
}
|
||||
|
||||
export const extractDomainsFromFelixDnsmasq = (line: string): string | null => {
|
||||
export function extractDomainsFromFelixDnsmasq(line: string): string | null {
|
||||
if (line.startsWith('server=/') && line.endsWith('/114.114.114.114')) {
|
||||
return line.slice(8, -16);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
export const parseFelixDnsmasqFromResp = async (resp: Response | NodeFetchResponse): Promise<string[]> => {
|
||||
export async function parseFelixDnsmasqFromResp(resp: Response | NodeFetchResponse): Promise<string[]> {
|
||||
const results: string[] = [];
|
||||
|
||||
for await (const line of createReadlineInterfaceFromResponse(resp)) {
|
||||
@ -26,9 +26,9 @@ export const parseFelixDnsmasqFromResp = async (resp: Response | NodeFetchRespon
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
}
|
||||
|
||||
export const parseFelixDnsmasq = async (url: string): Promise<string[]> => {
|
||||
export async function parseFelixDnsmasq(url: string): Promise<string[]> {
|
||||
const resp = await $fetch(url);
|
||||
return parseFelixDnsmasqFromResp(resp);
|
||||
};
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ const DEBUG_DOMAIN_TO_FIND: string | null = null; // example.com | null
|
||||
let foundDebugDomain = false;
|
||||
const temporaryBypass = typeof DEBUG_DOMAIN_TO_FIND === 'string';
|
||||
|
||||
const domainListLineCb = (l: string, set: string[], includeAllSubDomain: boolean, meta: string) => {
|
||||
function domainListLineCb(l: string, set: string[], includeAllSubDomain: boolean, meta: string) {
|
||||
let line = processLine(l);
|
||||
if (!line) return;
|
||||
line = line.toLowerCase();
|
||||
@ -39,7 +39,7 @@ const domainListLineCb = (l: string, set: string[], includeAllSubDomain: boolean
|
||||
}
|
||||
|
||||
set.push(includeAllSubDomain ? `.${line}` : line);
|
||||
};
|
||||
}
|
||||
|
||||
export function processDomainLists(
|
||||
span: Span,
|
||||
@ -71,7 +71,7 @@ export function processDomainLists(
|
||||
));
|
||||
}
|
||||
|
||||
const hostsLineCb = (l: string, set: string[], includeAllSubDomain: boolean, meta: string) => {
|
||||
function hostsLineCb(l: string, set: string[], includeAllSubDomain: boolean, meta: string) {
|
||||
const line = processLine(l);
|
||||
if (!line) {
|
||||
return;
|
||||
@ -91,7 +91,7 @@ const hostsLineCb = (l: string, set: string[], includeAllSubDomain: boolean, met
|
||||
}
|
||||
|
||||
set.push(includeAllSubDomain ? `.${domain}` : domain);
|
||||
};
|
||||
}
|
||||
|
||||
export function processHosts(
|
||||
span: Span,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export const processLine = (line: string): string | null => {
|
||||
export function processLine(line: string): string | null {
|
||||
if (!line) {
|
||||
return null;
|
||||
}
|
||||
@ -22,9 +22,9 @@ export const processLine = (line: string): string | null => {
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
};
|
||||
}
|
||||
|
||||
export const processLineFromReadline = async (rl: AsyncIterable<string>): Promise<string[]> => {
|
||||
export async function processLineFromReadline(rl: AsyncIterable<string>): Promise<string[]> {
|
||||
const res: string[] = [];
|
||||
for await (const line of rl) {
|
||||
const l: string | null = processLine(line);
|
||||
@ -33,4 +33,4 @@ export const processLineFromReadline = async (rl: AsyncIterable<string>): Promis
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
@ -125,6 +125,7 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
|
||||
}
|
||||
|
||||
private async addFromDomainsetPromise(source: AsyncIterable<string> | Iterable<string> | string[]) {
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable -- https://github.com/typescript-eslint/typescript-eslint/issues/10080
|
||||
for await (const line of source) {
|
||||
if (line[0] === '.') {
|
||||
this.addDomainSuffix(line);
|
||||
@ -140,6 +141,7 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
|
||||
}
|
||||
|
||||
private async addFromRulesetPromise(source: AsyncIterable<string> | Iterable<string>) {
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable -- https://github.com/typescript-eslint/typescript-eslint/issues/10080
|
||||
for await (const line of source) {
|
||||
const splitted = line.split(',');
|
||||
const type = splitted[0];
|
||||
@ -314,7 +316,7 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
|
||||
abstract mitmSgmodule?(): string[] | null;
|
||||
}
|
||||
|
||||
export const fileEqual = async (linesA: string[], source: AsyncIterable<string>): Promise<boolean> => {
|
||||
export async function fileEqual(linesA: string[], source: AsyncIterable<string>): Promise<boolean> {
|
||||
if (linesA.length === 0) {
|
||||
return false;
|
||||
}
|
||||
@ -350,7 +352,7 @@ export const fileEqual = async (linesA: string[], source: AsyncIterable<string>)
|
||||
|
||||
// The file becomes larger
|
||||
return !(index < linesA.length - 1);
|
||||
};
|
||||
}
|
||||
|
||||
export async function compareAndWriteFile(span: Span, linesA: string[], filePath: string) {
|
||||
let isEqual = true;
|
||||
|
||||
@ -2,10 +2,10 @@ import { isProbablyIpv4, isProbablyIpv6 } from './is-fast-ip';
|
||||
|
||||
const unsupported = Symbol('unsupported');
|
||||
|
||||
const toNumberTuple = <T extends string>(key: T, value: string): [T, number] | null => {
|
||||
function toNumberTuple<T extends string>(key: T, value: string): [T, number] | null {
|
||||
const tmp = Number(value);
|
||||
return Number.isNaN(tmp) ? null : [key, tmp];
|
||||
};
|
||||
}
|
||||
|
||||
// https://sing-box.sagernet.org/configuration/rule-set/source-format/
|
||||
export const PROCESSOR: Record<string, ((raw: string, type: string, value: string) => [key: keyof SingboxHeadlessRule, value: Required<SingboxHeadlessRule>[keyof SingboxHeadlessRule][number]] | null) | typeof unsupported> = {
|
||||
|
||||
@ -4,12 +4,12 @@
|
||||
import * as tldts from 'tldts-experimental';
|
||||
import { looseTldtsOpt } from '../constants/loose-tldts-opt';
|
||||
|
||||
export const compare = (a: string, b: string) => {
|
||||
export function compare(a: string, b: string) {
|
||||
if (a === b) return 0;
|
||||
return (a.length - b.length) || a.localeCompare(b);
|
||||
};
|
||||
}
|
||||
|
||||
export const buildParseDomainMap = (inputs: string[]) => {
|
||||
export function buildParseDomainMap(inputs: string[]) {
|
||||
const domainMap = new Map<string, string>();
|
||||
const subdomainMap = new Map<string, string>();
|
||||
|
||||
@ -24,13 +24,11 @@ export const buildParseDomainMap = (inputs: string[]) => {
|
||||
}
|
||||
|
||||
return { domainMap, subdomainMap };
|
||||
};
|
||||
}
|
||||
|
||||
export const sortDomains = (
|
||||
inputs: string[],
|
||||
export function sortDomains(inputs: string[],
|
||||
domainMap?: Map<string, string> | null,
|
||||
subdomainMap?: Map<string, string> | null
|
||||
) => {
|
||||
subdomainMap?: Map<string, string> | null) {
|
||||
if (!domainMap || !subdomainMap) {
|
||||
const { domainMap: dm, subdomainMap: sm } = buildParseDomainMap(inputs);
|
||||
domainMap = dm;
|
||||
@ -58,4 +56,4 @@ export const sortDomains = (
|
||||
};
|
||||
|
||||
return inputs.sort(sorter);
|
||||
};
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
* Simplified, optimized and add modified for 52 bit, which provides a larger hash space
|
||||
* and still making use of Javascript's 53-bit integer space.
|
||||
*/
|
||||
export const fnv1a52 = (str: string) => {
|
||||
export function fnv1a52(str: string) {
|
||||
const len = str.length;
|
||||
let i = 0,
|
||||
t0 = 0,
|
||||
@ -41,6 +41,6 @@ export const fnv1a52 = (str: string) => {
|
||||
+ v1 * 65536
|
||||
+ (v0 ^ (v3 >> 4))
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const stringHash = (payload: string) => fnv1a52(payload).toString(36) + payload.length.toString(36);
|
||||
|
||||
@ -19,7 +19,7 @@ export type TreeTypeArray = TreeType[];
|
||||
|
||||
type VoidOrVoidArray = void | VoidOrVoidArray[];
|
||||
|
||||
export const treeDir = async (rootPath: string): Promise<TreeTypeArray> => {
|
||||
export async function treeDir(rootPath: string): Promise<TreeTypeArray> {
|
||||
const tree: TreeTypeArray = [];
|
||||
|
||||
const walk = async (dir: string, node: TreeTypeArray, dirRelativeToRoot = ''): Promise<VoidOrVoidArray> => {
|
||||
@ -60,4 +60,4 @@ export const treeDir = async (rootPath: string): Promise<TreeTypeArray> => {
|
||||
await walk(rootPath, tree);
|
||||
|
||||
return tree;
|
||||
};
|
||||
}
|
||||
|
||||
@ -13,10 +13,8 @@ type TrieNode<Meta = any> = [
|
||||
Meta /** meta */
|
||||
];
|
||||
|
||||
const deepTrieNodeToJSON = (
|
||||
node: TrieNode,
|
||||
unpackMeta: ((meta?: any) => string) | undefined
|
||||
) => {
|
||||
function deepTrieNodeToJSON(node: TrieNode,
|
||||
unpackMeta: ((meta?: any) => string) | undefined) {
|
||||
const obj: Record<string, any> = {};
|
||||
if (node[0]) {
|
||||
obj['[start]'] = node[0];
|
||||
@ -32,11 +30,11 @@ const deepTrieNodeToJSON = (
|
||||
obj[key] = deepTrieNodeToJSON(value, unpackMeta);
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
const createNode = <Meta = any>(parent: TrieNode | null = null): TrieNode => [false, parent, new Map<string, TrieNode>(), null] as TrieNode<Meta>;
|
||||
|
||||
export const hostnameToTokens = (hostname: string): string[] => {
|
||||
export function hostnameToTokens(hostname: string): string[] {
|
||||
const tokens = hostname.split('.');
|
||||
const results: string[] = [];
|
||||
let token = '';
|
||||
@ -51,9 +49,9 @@ export const hostnameToTokens = (hostname: string): string[] => {
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
}
|
||||
|
||||
const walkHostnameTokens = (hostname: string, onToken: (token: string) => boolean | null): boolean | null => {
|
||||
function walkHostnameTokens(hostname: string, onToken: (token: string) => boolean | null): boolean | null {
|
||||
const tokens = hostname.split('.');
|
||||
let token = '';
|
||||
|
||||
@ -78,7 +76,7 @@ const walkHostnameTokens = (hostname: string, onToken: (token: string) => boolea
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
interface FindSingleChildLeafResult<Meta> {
|
||||
node: TrieNode<Meta>,
|
||||
|
||||
@ -34,7 +34,7 @@ export interface Span {
|
||||
readonly traceResult: TraceResult
|
||||
}
|
||||
|
||||
export const createSpan = (name: string, parentTraceResult?: TraceResult): Span => {
|
||||
export function createSpan(name: string, parentTraceResult?: TraceResult): Span {
|
||||
const start = performance.now();
|
||||
|
||||
let curTraceResult: TraceResult;
|
||||
@ -93,11 +93,12 @@ export const createSpan = (name: string, parentTraceResult?: TraceResult): Span
|
||||
|
||||
// eslint-disable-next-line sukka/no-redundant-variable -- self reference
|
||||
return span;
|
||||
};
|
||||
}
|
||||
|
||||
export const dummySpan = createSpan('');
|
||||
|
||||
export const task = (importMetaMain: boolean, importMetaPath: string) => <T>(fn: (span: Span) => Promise<T>, customName?: string) => {
|
||||
export function task(importMetaMain: boolean, importMetaPath: string) {
|
||||
return <T>(fn: (span: Span) => Promise<T>, customName?: string) => {
|
||||
const taskName = customName ?? basename(importMetaPath, extname(importMetaPath));
|
||||
|
||||
const dummySpan = createSpan(taskName);
|
||||
@ -114,6 +115,7 @@ export const task = (importMetaMain: boolean, importMetaPath: string) => <T>(fn:
|
||||
return fn(dummySpan);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// const isSpan = (obj: any): obj is Span => {
|
||||
// return typeof obj === 'object' && obj && spanTag in obj;
|
||||
@ -128,10 +130,10 @@ export const task = (importMetaMain: boolean, importMetaPath: string) => <T>(fn:
|
||||
// };
|
||||
// };
|
||||
|
||||
export const printTraceResult = (traceResult: TraceResult = rootTraceResult) => {
|
||||
export function printTraceResult(traceResult: TraceResult = rootTraceResult) {
|
||||
printStats(traceResult.children);
|
||||
printTree(traceResult, node => `${node.name} ${picocolors.bold(`${(node.end - node.start).toFixed(3)}ms`)}`);
|
||||
};
|
||||
}
|
||||
|
||||
function printTree(initialTree: TraceResult, printNode: (node: TraceResult, branch: string) => string) {
|
||||
function printBranch(tree: TraceResult, branch: string, isGraphHead: boolean, isChildOfLastBranch: boolean) {
|
||||
|
||||
@ -7,7 +7,7 @@ import { parseFelixDnsmasq } from './lib/parse-dnsmasq';
|
||||
import { SOURCE_DIR } from './constants/dir';
|
||||
import { $fetch } from './lib/make-fetch-happen';
|
||||
|
||||
export const parseDomesticList = async () => {
|
||||
export async function parseDomesticList() {
|
||||
const trie = createTrie(await parseFelixDnsmasq('https://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/master/accelerated-domains.china.conf'));
|
||||
|
||||
const top5000 = new Set<string>();
|
||||
@ -60,4 +60,4 @@ export const parseDomesticList = async () => {
|
||||
// ]);
|
||||
|
||||
console.log(notIncludedDomestic.size, notIncludedDomestic);
|
||||
};
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ import path from 'node:path';
|
||||
import { SOURCE_DIR } from './constants/dir';
|
||||
import { $fetch } from './lib/make-fetch-happen';
|
||||
|
||||
export const parseGfwList = async () => {
|
||||
export async function parseGfwList() {
|
||||
const whiteSet = new Set<string>();
|
||||
const blackSet = new Set<string>();
|
||||
|
||||
@ -120,4 +120,4 @@ export const parseGfwList = async () => {
|
||||
trie,
|
||||
top500Gfwed
|
||||
] as const;
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user