浏览代码

Chore: make ESLint Happy

SukkaW 1 年之前
父节点
当前提交
e40979e50e

+ 35 - 33
Build/build-common.ts

@@ -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[] = [];
-
-  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;
-      }
+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;
+
+    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_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_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;
-      }
+        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);
+        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

+ 2 - 2
Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts

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

+ 4 - 4
Build/build-public.ts

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

+ 2 - 2
Build/build-speedtest-domainset.ts

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

+ 25 - 25
Build/build-stream-service.ts

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

+ 4 - 4
Build/lib/aho-corasick.ts

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

+ 2 - 2
Build/lib/async-write-to-stream.ts

@@ -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 - 2
Build/lib/bitwise.ts

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

+ 2 - 2
Build/lib/cache-apply.ts

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

+ 2 - 2
Build/lib/cache-filesystem.ts

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

+ 4 - 4
Build/lib/convert-clash-meta-mrs.ts

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

+ 2 - 2
Build/lib/create-file.test.ts

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

+ 12 - 10
Build/lib/fetch-assets.ts

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

+ 20 - 20
Build/lib/fetch-retry.ts

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

+ 4 - 4
Build/lib/fetch-text-by-line.ts

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

+ 17 - 15
Build/lib/get-phishing-domains.ts

@@ -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, '|')));
+    const cacheHash = span.traceChildSync('get hash', () => stringHash(fastStringArrayJoin(domainArr, '|')));
 
-  return span.traceChildAsync(
-    'process phishing domain set',
-    () => processPhihsingDomains(domainArr, cacheHash)
-  );
-});
+    return span.traceChildAsync(
+      'process phishing domain set',
+      () => processPhihsingDomains(domainArr, cacheHash)
+    );
+  });
+}
 
 async function processPhihsingDomains(domainArr: string[], cacheHash = '') {
   return fsFetchCache.apply(

+ 2 - 2
Build/lib/memo-promise.ts

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

+ 18 - 16
Build/lib/misc.ts

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

+ 2 - 2
Build/lib/normalize-domain.ts

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

+ 8 - 8
Build/lib/parse-dnsmasq.ts

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

+ 4 - 4
Build/lib/parse-filter.ts

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

+ 4 - 4
Build/lib/process-line.ts

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

+ 4 - 2
Build/lib/rules/base.ts

@@ -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 - 2
Build/lib/singbox.ts

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

+ 7 - 9
Build/lib/stable-sort-domain.ts

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

+ 2 - 2
Build/lib/string-hash.ts

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

+ 2 - 2
Build/lib/tree-dir.ts

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

+ 7 - 9
Build/lib/trie.ts

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

+ 20 - 18
Build/trace/index.ts

@@ -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));
-
-  const dummySpan = createSpan(taskName);
-  if (importMetaMain) {
-    fn(dummySpan).finally(() => {
-      console.log(wtf.dump());
-    });
-  }
+export function task(importMetaMain: boolean, importMetaPath: string) {
+  return <T>(fn: (span: Span) => Promise<T>, customName?: string) => {
+    const taskName = customName ?? basename(importMetaPath, extname(importMetaPath));
 
-  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) {

+ 2 - 2
Build/validate-domestic.ts

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

+ 2 - 2
Build/validate-gfwlist.ts

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