Chore: make ESLint Happy

This commit is contained in:
SukkaW 2024-10-10 21:40:54 +08:00
parent 40cda6997a
commit e40979e50e
31 changed files with 228 additions and 220 deletions

View File

@ -64,46 +64,48 @@ 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 () => {
const lines: string[] = [];
function processFile(span: Span, sourcePath: string) {
return span.traceChildAsync(`process file: ${sourcePath}`, async () => {
const lines: string[] = [];
let title = '';
const descriptions: string[] = [];
let sgmodulePathname: string | null = null;
let title = '';
const descriptions: string[] = [];
let sgmodulePathname: string | null = null;
try {
for await (const line of readFileByLine(sourcePath)) {
if (line.startsWith(MAGIC_COMMAND_SKIP)) {
return $skip;
}
if (line.startsWith(MAGIC_COMMAND_TITLE)) {
title = line.slice(MAGIC_COMMAND_TITLE.length).trim();
continue;
}
if (line.startsWith(MAGIC_COMMAND_DESCRIPTION)) {
descriptions.push(line.slice(MAGIC_COMMAND_DESCRIPTION.length).trim());
continue;
}
if (line.startsWith(MAGIC_COMMAND_SGMODULE_MITM_HOSTNAMES)) {
sgmodulePathname = line.slice(MAGIC_COMMAND_SGMODULE_MITM_HOSTNAMES.length).trim();
continue;
}
const l = processLine(line);
if (l) {
lines.push(l);
try {
for await (const line of readFileByLine(sourcePath)) {
if (line.startsWith(MAGIC_COMMAND_SKIP)) {
return $skip;
}
if (line.startsWith(MAGIC_COMMAND_TITLE)) {
title = line.slice(MAGIC_COMMAND_TITLE.length).trim();
continue;
}
if (line.startsWith(MAGIC_COMMAND_DESCRIPTION)) {
descriptions.push(line.slice(MAGIC_COMMAND_DESCRIPTION.length).trim());
continue;
}
if (line.startsWith(MAGIC_COMMAND_SGMODULE_MITM_HOSTNAMES)) {
sgmodulePathname = line.slice(MAGIC_COMMAND_SGMODULE_MITM_HOSTNAMES.length).trim();
continue;
}
const l = processLine(line);
if (l) {
lines.push(l);
}
}
} catch (e) {
console.error('Error processing', sourcePath);
console.trace(e);
}
} catch (e) {
console.error('Error processing', sourcePath);
console.trace(e);
}
return [title, descriptions, lines, sgmodulePathname] as const;
});
return [title, descriptions, lines, sgmodulePathname] as const;
});
}
function transformDomainset(parentSpan: Span, sourcePath: string, relativePath: string) {
return parentSpan

View File

@ -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'));

View File

@ -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`

View File

@ -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')

View File

@ -6,33 +6,33 @@ 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}`)
.withDescription([
...SHARED_DESCRIPTION,
'',
...streamServices.map((i) => `- ${i.name}`)
])
.addFromRuleset(streamServices.flatMap((i) => i.rules))
.write(),
// IP
new RulesetOutput(childSpan, fileId, 'ip')
.withTitle(`Sukka's Ruleset - Stream Services IPs: ${title}`)
.withDescription([
...SHARED_DESCRIPTION,
'',
...streamServices.map((i) => `- ${i.name}`)
])
.bulkAddCIDR4NoResolve(streamServices.flatMap(i => i.ip?.v4 ?? []))
.bulkAddCIDR6NoResolve(streamServices.flatMap(i => i.ip?.v6 ?? []))
.write()
]));
new RulesetOutput(childSpan, fileId, 'non_ip')
.withTitle(`Sukka's Ruleset - Stream Services: ${title}`)
.withDescription([
...SHARED_DESCRIPTION,
'',
...streamServices.map((i) => `- ${i.name}`)
])
.addFromRuleset(streamServices.flatMap((i) => i.rules))
.write(),
// IP
new RulesetOutput(childSpan, fileId, 'ip')
.withTitle(`Sukka's Ruleset - Stream Services IPs: ${title}`)
.withDescription([
...SHARED_DESCRIPTION,
'',
...streamServices.map((i) => `- ${i.name}`)
])
.bulkAddCIDR4NoResolve(streamServices.flatMap(i => i.ip?.v4 ?? []))
.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),

View File

@ -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;

View File

@ -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
}
};
}

View File

@ -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;

View File

@ -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;
}
};
};
}

View File

@ -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 + '$';
};
}

View File

@ -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);
}
};
}

View File

@ -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(

View File

@ -26,19 +26,21 @@ export class CustomNoETagFallbackError extends Error {
}
}
export const sleepWithAbort = (ms: number, signal: AbortSignal) => new Promise<void>((resolve, reject) => {
if (signal.aborted) {
reject(signal.reason as Error);
return;
}
export function sleepWithAbort(ms: number, signal: AbortSignal) {
return new Promise<void>((resolve, reject) => {
if (signal.aborted) {
reject(signal.reason as Error);
return;
}
signal.addEventListener('abort', stop, { once: true });
signal.addEventListener('abort', stop, { once: true });
// eslint-disable-next-line sukka/prefer-timer-id -- node:timers/promises
setTimeout(ms, undefined, { ref: false }).then(resolve).catch(reject).finally(() => signal.removeEventListener('abort', stop));
// eslint-disable-next-line sukka/prefer-timer-id -- node:timers/promises
setTimeout(ms, undefined, { ref: false }).then(resolve).catch(reject).finally(() => signal.removeEventListener('abort', stop));
function stop(this: AbortSignal) { reject(this.reason as Error); }
});
function stop(this: AbortSignal) { reject(this.reason as Error); }
});
}
export async function fetchAssets(url: string, fallbackUrls: string[] | readonly string[]) {
const controller = new AbortController();

View File

@ -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;

View File

@ -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);

View File

@ -101,25 +101,27 @@ const lowKeywords = createKeywordFilter([
const cacheKey = createCacheKey(__filename);
export const getPhishingDomains = (parentSpan: Span) => parentSpan.traceChild('get phishing domains').traceAsyncFn(async (span) => {
const domainArr = await span.traceChildAsync('download/parse/merge phishing domains', async (curSpan) => {
const domainArr: string[] = [];
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[] = [];
(await Promise.all(PHISHING_DOMAIN_LISTS_EXTRA.map(entry => processDomainLists(curSpan, ...entry, cacheKey))))
.forEach(appendArrayInPlaceCurried(domainArr));
(await Promise.all(PHISHING_HOSTS_EXTRA.map(entry => processHosts(curSpan, ...entry, cacheKey))))
.forEach(appendArrayInPlaceCurried(domainArr));
(await Promise.all(PHISHING_DOMAIN_LISTS_EXTRA.map(entry => processDomainLists(curSpan, ...entry, cacheKey))))
.forEach(appendArrayInPlaceCurried(domainArr));
(await Promise.all(PHISHING_HOSTS_EXTRA.map(entry => processHosts(curSpan, ...entry, cacheKey))))
.forEach(appendArrayInPlaceCurried(domainArr));
return domainArr;
return domainArr;
});
const cacheHash = span.traceChildSync('get hash', () => stringHash(fastStringArrayJoin(domainArr, '|')));
return span.traceChildAsync(
'process phishing domain set',
() => processPhihsingDomains(domainArr, cacheHash)
);
});
const cacheHash = span.traceChildSync('get hash', () => stringHash(fastStringArrayJoin(domainArr, '|')));
return span.traceChildAsync(
'process phishing domain set',
() => processPhihsingDomains(domainArr, cacheHash)
);
});
}
async function processPhihsingDomains(domainArr: string[], cacheHash = '') {
return fsFetchCache.apply(

View File

@ -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;
};
};
}

View File

@ -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') => [
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 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;
};
}

View File

@ -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;
};
}

View File

@ -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);
};
}

View File

@ -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,

View File

@ -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;
};
}

View File

@ -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;

View File

@ -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> = {

View File

@ -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);
};
}

View File

@ -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);

View File

@ -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;
};
}

View File

@ -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>,

View File

@ -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,27 +93,29 @@ 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) => {
const taskName = customName ?? basename(importMetaPath, extname(importMetaPath));
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);
if (importMetaMain) {
fn(dummySpan).finally(() => {
console.log(wtf.dump());
});
}
return async (span?: Span) => {
if (span) {
return span.traceChildAsync(taskName, fn);
const dummySpan = createSpan(taskName);
if (importMetaMain) {
fn(dummySpan).finally(() => {
console.log(wtf.dump());
});
}
return fn(dummySpan);
return async (span?: Span) => {
if (span) {
return span.traceChildAsync(taskName, 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) {

View File

@ -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);
};
}

View File

@ -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;
};
}