Refactor: new output

This commit is contained in:
SukkaW 2024-09-20 23:29:37 +08:00
parent 5448d677fe
commit b119fa652d
7 changed files with 439 additions and 269 deletions

View File

@ -1,11 +1,9 @@
// @ts-check
import { createRuleset } from './lib/create-file';
import { parseFelixDnsmasq } from './lib/parse-dnsmasq';
import { task } from './trace';
import { SHARED_DESCRIPTION } from './lib/constants';
import { createMemoizedPromise } from './lib/memo-promise';
import { TTL, deserializeArray, fsFetchCache, serializeArray, createCacheKey } from './lib/cache-filesystem';
import { output } from './lib/misc';
import { DomainsetOutput } from './lib/create-file-new';
const cacheKey = createCacheKey(__filename);
@ -23,24 +21,16 @@ export const getAppleCdnDomainsPromise = createMemoizedPromise(() => fsFetchCach
export const buildAppleCdn = task(require.main === module, __filename)(async (span) => {
const res: string[] = await span.traceChildPromise('get apple cdn domains', getAppleCdnDomainsPromise());
const description = [
...SHARED_DESCRIPTION,
'',
'This file contains Apple\'s domains using their China mainland CDN servers.',
'',
'Data from:',
' - https://github.com/felixonmars/dnsmasq-china-list'
];
const domainset = res.map(i => `.${i}`);
return createRuleset(
span,
'Sukka\'s Ruleset - Apple CDN',
description,
new Date(),
domainset,
'domainset',
output('apple_cdn', 'domainset')
);
return new DomainsetOutput(span, 'apple_cdn')
.withTitle('Sukka\'s Ruleset - Apple CDN')
.withDescription([
...SHARED_DESCRIPTION,
'',
'This file contains Apple\'s domains using their China mainland CDN servers.',
'',
'Data from:',
' - https://github.com/felixonmars/dnsmasq-china-list'
])
.bulkAddDomainSuffix(res)
.write();
});

View File

@ -1,16 +1,13 @@
import path from 'node:path';
import { createRuleset } from './lib/create-file';
import { readFileIntoProcessedArray } from './lib/fetch-text-by-line';
import { createTrie } from './lib/trie';
import { task } from './trace';
import { SHARED_DESCRIPTION } from './lib/constants';
import { getPublicSuffixListTextPromise } from './lib/download-publicsuffixlist';
import { domainsetDeduper } from './lib/domain-deduper';
import { appendArrayInPlace } from './lib/append-array-in-place';
import { sortDomains } from './lib/stable-sort-domain';
import { output } from './lib/misc';
import { SOURCE_DIR } from './constants/dir';
import { processLine } from './lib/process-line';
import { DomainsetOutput } from './lib/create-file-new';
const getS3OSSDomainsPromise = (async (): Promise<string[]> => {
const trie = createTrie(
@ -77,31 +74,24 @@ export const buildCdnDownloadConf = task(require.main === module, __filename)(as
appendArrayInPlace(downloadDomainSet, steamDomainSet);
return Promise.all([
createRuleset(
span,
'Sukka\'s Ruleset - CDN Domains',
[
new DomainsetOutput(span, 'cdn')
.withTitle('Sukka\'s Ruleset - CDN Domains')
.withDescription([
...SHARED_DESCRIPTION,
'',
'This file contains object storage and static assets CDN domains.'
],
new Date(),
sortDomains(domainsetDeduper(cdnDomainsList)),
'domainset',
output('cdn', 'domainset')
),
createRuleset(
span,
'Sukka\'s Ruleset - Large Files Hosting Domains',
[
])
.addFromDomainset(cdnDomainsList)
.write(),
new DomainsetOutput(span, 'download')
.withTitle('Sukka\'s Ruleset - Large Files Hosting Domains')
.withDescription([
...SHARED_DESCRIPTION,
'',
'This file contains domains for software updating & large file hosting.'
],
new Date(),
sortDomains(domainsetDeduper(downloadDomainSet)),
'domainset',
output('download', 'domainset')
)
])
.addFromDomainset(downloadDomainSet)
.write()
]);
});

View File

@ -1,5 +1,4 @@
import { fetchRemoteTextByLine } from './lib/fetch-text-by-line';
import { createRuleset } from './lib/create-file';
import { processLineFromReadline } from './lib/process-line';
import { task } from './trace';
@ -7,7 +6,7 @@ import { exclude } from 'fast-cidr-tools';
import { createMemoizedPromise } from './lib/memo-promise';
import { CN_CIDR_NOT_INCLUDED_IN_CHNROUTE, NON_CN_CIDR_INCLUDED_IN_CHNROUTE } from './constants/cidr';
import { appendArrayInPlace } from './lib/append-array-in-place';
import { output } from './lib/misc';
import { IPListOutput } from './lib/create-file-new';
export const getChnCidrPromise = createMemoizedPromise(async () => {
const cidr4 = await processLineFromReadline(await fetchRemoteTextByLine('https://raw.githubusercontent.com/misakaio/chnroutes2/master/chnroutes.txt'));
@ -28,31 +27,22 @@ export const buildChnCidr = task(require.main === module, __filename)(async (spa
''
];
// Can not use createRuleset here, as Clash support advanced ipset syntax
return Promise.all([
createRuleset(
span,
'Sukka\'s Ruleset - Mainland China IPv4 CIDR',
[
new IPListOutput(span, 'china_ip', false)
.withTitle('Sukka\'s Ruleset - Mainland China IPv4 CIDR')
.withDescription([
...description,
'Data from https://misaka.io (misakaio @ GitHub)'
],
new Date(),
filteredCidr4,
'ipcidr',
output('china_ip', 'ip')
),
createRuleset(
span,
'Sukka\'s Ruleset - Mainland China IPv6 CIDR',
[
])
.bulkAddCIDR4(filteredCidr4)
.write(),
new IPListOutput(span, 'china_ip_ipv6', false)
.withTitle('Sukka\'s Ruleset - Mainland China IPv6 CIDR')
.withDescription([
...description,
'Data from https://github.com/gaoyifan/china-operator-ip'
],
new Date(),
cidr6,
'ipcidr6',
output('china_ip_ipv6', 'ip')
)
])
.bulkAddCIDR6(cidr6)
.write()
]);
});

View File

@ -4,13 +4,13 @@ import * as path from 'node:path';
import { readFileByLine } from './lib/fetch-text-by-line';
import { processLine } from './lib/process-line';
import { createRuleset } from './lib/create-file';
import { domainsetDeduper } from './lib/domain-deduper';
import type { Span } from './trace';
import { task } from './trace';
import { SHARED_DESCRIPTION } from './lib/constants';
import { fdir as Fdir } from 'fdir';
import { appendArrayInPlace } from './lib/append-array-in-place';
import { OUTPUT_CLASH_DIR, OUTPUT_SINGBOX_DIR, OUTPUT_SURGE_DIR, SOURCE_DIR } from './constants/dir';
import { DomainsetOutput } from './lib/create-file-new';
const MAGIC_COMMAND_SKIP = '# $ custom_build_script';
const MAGIC_COMMAND_TITLE = '# $ meta_title ';
@ -113,10 +113,8 @@ function transformDomainset(parentSpan: Span, sourcePath: string, relativePath:
const res = await processFile(span, sourcePath);
if (res === $skip) return;
const clashFileBasename = relativePath.slice(0, -path.extname(relativePath).length);
const id = path.basename(relativePath).slice(0, -path.extname(relativePath).length);
const [title, descriptions, lines] = res;
const deduped = domainsetDeduper(lines);
let description: string[];
if (descriptions.length) {
@ -127,19 +125,11 @@ function transformDomainset(parentSpan: Span, sourcePath: string, relativePath:
description = SHARED_DESCRIPTION;
}
return createRuleset(
span,
title,
description,
new Date(),
deduped,
'domainset',
[
path.resolve(OUTPUT_SURGE_DIR, relativePath),
path.resolve(OUTPUT_CLASH_DIR, `${clashFileBasename}.txt`),
path.resolve(OUTPUT_SINGBOX_DIR, `${clashFileBasename}.json`)
]
);
return new DomainsetOutput(span, id)
.withTitle(title)
.withDescription(description)
.addFromDomainset(lines)
.write();
}
);
}

View File

@ -1,6 +1,4 @@
import path from 'node:path';
import { createRuleset } from './lib/create-file';
import { sortDomains } from './lib/stable-sort-domain';
import { Sema } from 'async-sema';
import { getHostname } from 'tldts';
@ -10,8 +8,132 @@ import { SHARED_DESCRIPTION } from './lib/constants';
import { readFileIntoProcessedArray } from './lib/fetch-text-by-line';
import { TTL, deserializeArray, fsFetchCache, serializeArray, createCacheKey } from './lib/cache-filesystem';
import { createTrie } from './lib/trie';
import { output } from './lib/misc';
import { DomainsetOutput } from './lib/create-file-new';
import { OUTPUT_SURGE_DIR } from './constants/dir';
const KEYWORDS = [
'Hong Kong',
'Taiwan',
'China Telecom',
'China Mobile',
'China Unicom',
'Japan',
'Tokyo',
'Singapore',
'Korea',
'Seoul',
'Canada',
'Toronto',
'Montreal',
'Los Ang',
'San Jos',
'Seattle',
'New York',
'Dallas',
'Miami',
'Berlin',
'Frankfurt',
'London',
'Paris',
'Amsterdam',
'Moscow',
'Australia',
'Sydney',
'Brazil',
'Turkey'
];
const PREDEFINE_DOMAINS = [
// speedtest.net
'.speedtest.net',
'.speedtestcustom.com',
'.ooklaserver.net',
'.speed.misaka.one',
'.speedtest.rt.ru',
'.speedtest.aptg.com.tw',
'.speedtest.gslnetworks.com',
'.speedtest.jsinfo.net',
'.speedtest.i3d.net',
'.speedtestkorea.com',
'.speedtest.telus.com',
'.speedtest.telstra.net',
'.speedtest.clouvider.net',
'.speedtest.idv.tw',
'.speedtest.frontier.com',
'.speedtest.orange.fr',
'.speedtest.centurylink.net',
'.srvr.bell.ca',
'.speedtest.contabo.net',
'speedtest.hk.chinamobile.com',
'speedtestbb.hk.chinamobile.com',
'.hizinitestet.com',
'.linknetspeedtest.net.br',
'speedtest.rit.edu',
'speedtest.ropa.de',
'speedtest.sits.su',
'speedtest.tigo.cr',
'speedtest.upp.com',
'.speedtest.pni.tw',
'.speed.pfm.gg',
'.speedtest.faelix.net',
'.speedtest.labixe.net',
'.speedtest.warian.net',
'.speedtest.starhub.com',
'.speedtest.gibir.net.tr',
'.speedtest.ozarksgo.net',
'.speedtest.exetel.com.au',
'.speedtest.sbcglobal.net',
'.speedtest.leaptel.com.au',
'.speedtest.windstream.net',
'.speedtest.vodafone.com.au',
'.speedtest.rascom.ru',
'.speedtest.dchost.com',
'.speedtest.highnet.com',
'.speedtest.seattle.wa.limewave.net',
'.speedtest.optitel.com.au',
'.speednet.net.tr',
'.speedtest.angolacables.co.ao',
'.ookla-speedtest.fsr.com',
'.speedtest.comnet.com.tr',
'.speedtest.gslnetworks.com.au',
'.test.gslnetworks.com.au',
'.speedtest.gslnetworks.com',
'.speedtestunonet.com.br',
'.speedtest.alagas.net',
'speedtest.surfshark.com',
'.speedtest.aarnet.net.au',
'.ookla.rcp.net',
'.ookla-speedtests.e2ro.com',
'.speedtest.com.sg',
'.ookla.ddnsgeek.com',
'.speedtest.pni.tw',
'.speedtest.cmcnetworks.net',
'.speedtestwnet.com.br',
// Cloudflare
'.speed.cloudflare.com',
// Wi-Fi Man
'.wifiman.com',
'.wifiman.me',
'.wifiman.ubncloud.com',
// Fast.com
'.fast.com',
// MacPaw
'speedtest.macpaw.com',
// speedtestmaster
'.netspeedtestmaster.com',
// Google Search Result of "speedtest", powered by this
'.measurement-lab.org',
'.measurementlab.net',
// Google Fiber legacy speedtest site (new fiber speedtest use speedtestcustom.com)
'.speed.googlefiber.net',
// librespeed
'.backend.librespeed.org',
// Apple,
'mensura.cdn-apple.com', // From netQuality command
// OpenSpeedtest
'open.cachefly.net' // This is also used for openspeedtest server download
];
const s = new Sema(2);
const cacheKey = createCacheKey(__filename);
@ -85,170 +207,24 @@ const querySpeedtestApi = async (keyword: string): Promise<Array<string | null>>
};
export const buildSpeedtestDomainSet = task(require.main === module, __filename)(async (span) => {
const domainTrie = createTrie(
[
// speedtest.net
'.speedtest.net',
'.speedtestcustom.com',
'.ooklaserver.net',
'.speed.misaka.one',
'.speedtest.rt.ru',
'.speedtest.aptg.com.tw',
'.speedtest.gslnetworks.com',
'.speedtest.jsinfo.net',
'.speedtest.i3d.net',
'.speedtestkorea.com',
'.speedtest.telus.com',
'.speedtest.telstra.net',
'.speedtest.clouvider.net',
'.speedtest.idv.tw',
'.speedtest.frontier.com',
'.speedtest.orange.fr',
'.speedtest.centurylink.net',
'.srvr.bell.ca',
'.speedtest.contabo.net',
'speedtest.hk.chinamobile.com',
'speedtestbb.hk.chinamobile.com',
'.hizinitestet.com',
'.linknetspeedtest.net.br',
'speedtest.rit.edu',
'speedtest.ropa.de',
'speedtest.sits.su',
'speedtest.tigo.cr',
'speedtest.upp.com',
'.speedtest.pni.tw',
'.speed.pfm.gg',
'.speedtest.faelix.net',
'.speedtest.labixe.net',
'.speedtest.warian.net',
'.speedtest.starhub.com',
'.speedtest.gibir.net.tr',
'.speedtest.ozarksgo.net',
'.speedtest.exetel.com.au',
'.speedtest.sbcglobal.net',
'.speedtest.leaptel.com.au',
'.speedtest.windstream.net',
'.speedtest.vodafone.com.au',
'.speedtest.rascom.ru',
'.speedtest.dchost.com',
'.speedtest.highnet.com',
'.speedtest.seattle.wa.limewave.net',
'.speedtest.optitel.com.au',
'.speednet.net.tr',
'.speedtest.angolacables.co.ao',
'.ookla-speedtest.fsr.com',
'.speedtest.comnet.com.tr',
'.speedtest.gslnetworks.com.au',
'.test.gslnetworks.com.au',
'.speedtest.gslnetworks.com',
'.speedtestunonet.com.br',
'.speedtest.alagas.net',
'speedtest.surfshark.com',
'.speedtest.aarnet.net.au',
'.ookla.rcp.net',
'.ookla-speedtests.e2ro.com',
'.speedtest.com.sg',
'.ookla.ddnsgeek.com',
'.speedtest.pni.tw',
'.speedtest.cmcnetworks.net',
'.speedtestwnet.com.br',
// Cloudflare
'.speed.cloudflare.com',
// Wi-Fi Man
'.wifiman.com',
'.wifiman.me',
'.wifiman.ubncloud.com',
// Fast.com
'.fast.com',
// MacPaw
'speedtest.macpaw.com',
// speedtestmaster
'.netspeedtestmaster.com',
// Google Search Result of "speedtest", powered by this
'.measurement-lab.org',
'.measurementlab.net',
// Google Fiber legacy speedtest site (new fiber speedtest use speedtestcustom.com)
'.speed.googlefiber.net',
// librespeed
'.backend.librespeed.org',
// Apple,
'mensura.cdn-apple.com', // From netQuality command
// OpenSpeedtest
'open.cachefly.net' // This is also used for openspeedtest server download
],
true
);
const output = new DomainsetOutput(span, 'speedtest')
.withTitle('Sukka\'s Ruleset - Speedtest Domains')
.withDescription([
...SHARED_DESCRIPTION,
'',
'This file contains common speedtest endpoints.'
])
.addFromDomainset(PREDEFINE_DOMAINS)
.addFromDomainset(await readFileIntoProcessedArray(path.resolve(OUTPUT_SURGE_DIR, 'domainset/speedtest.conf')));
await span.traceChildAsync(
'fetch previous speedtest domainset',
async () => {
try {
(
await readFileIntoProcessedArray(path.resolve(__dirname, '../List/domainset/speedtest.conf'))
) .forEach(line => {
const hn = getHostname(line, { detectIp: false, validateHostname: true });
if (hn) {
domainTrie.add(hn);
}
});
} catch { }
}
);
await Promise.all([
'Hong Kong',
'Taiwan',
'China Telecom',
'China Mobile',
'China Unicom',
'Japan',
'Tokyo',
'Singapore',
'Korea',
'Seoul',
'Canada',
'Toronto',
'Montreal',
'Los Ang',
'San Jos',
'Seattle',
'New York',
'Dallas',
'Miami',
'Berlin',
'Frankfurt',
'London',
'Paris',
'Amsterdam',
'Moscow',
'Australia',
'Sydney',
'Brazil',
'Turkey'
].map((keyword) => span.traceChildAsync(
await Promise.all(KEYWORDS.map((keyword) => span.traceChildAsync(
`fetch speedtest endpoints: ${keyword}`,
() => querySpeedtestApi(keyword)
).then(hostnameGroup => hostnameGroup.forEach(hostname => {
if (hostname) {
domainTrie.add(hostname);
output.addDomain(hostname);
}
}))));
const deduped = span.traceChildSync('sort result', () => sortDomains(domainTrie.dump()));
const description = [
...SHARED_DESCRIPTION,
'',
'This file contains common speedtest endpoints.'
];
return createRuleset(
span,
'Sukka\'s Ruleset - Speedtest Domains',
description,
new Date(),
deduped,
'domainset',
output('speedtest', 'domainset')
);
return output.write();
});

View File

@ -0,0 +1,249 @@
import path from 'node:path';
import type { Span } from '../trace';
import { surgeDomainsetToClashDomainset } from './clash';
import { compareAndWriteFile, withBannerArray } from './create-file';
import { ipCidrListToSingbox, surgeDomainsetToSingbox } from './singbox';
import { sortDomains } from './stable-sort-domain';
import { createTrie } from './trie';
import { invariant } from 'foxact/invariant';
import { OUTPUT_CLASH_DIR, OUTPUT_SINGBOX_DIR, OUTPUT_SURGE_DIR } from '../constants/dir';
import stringify from 'json-stringify-pretty-compact';
import { appendArrayInPlace } from './append-array-in-place';
abstract class RuleOutput {
protected domainTrie = createTrie<unknown>(null, true);
protected domainKeywords = new Set<string>();
protected domainWildcard = new Set<string>();
protected ipcidr = new Set<string>();
protected ipcidrNoResolve = new Set<string>();
protected ipcidr6 = new Set<string>();
protected ipcidr6NoResolve = new Set<string>();
protected otherRules = new Set<string>();
protected abstract type: 'domainset' | 'non_ip' | 'ip';
protected pendingPromise = Promise.resolve();
static jsonToLines(this: void, json: unknown): string[] {
return stringify(json).split('\n');
}
constructor(
protected readonly span: Span,
protected readonly id: string
) {}
protected title: string | null = null;
withTitle(title: string) {
this.title = title;
return this;
}
protected description: string[] | readonly string[] | null = null;
withDescription(description: string[] | readonly string[]) {
this.description = description;
return this;
}
protected date = new Date();
withDate(date: Date) {
this.date = date;
return this;
}
protected apexDomainMap: Map<string, string> | null = null;
protected subDomainMap: Map<string, string> | null = null;
withDomainMap(apexDomainMap: Map<string, string>, subDomainMap: Map<string, string>) {
this.apexDomainMap = apexDomainMap;
this.subDomainMap = subDomainMap;
return this;
}
addDomain(domain: string) {
this.domainTrie.add(domain);
return this;
}
addDomainSuffix(domain: string) {
this.domainTrie.add(domain[0] === '.' ? domain : '.' + domain);
return this;
}
bulkAddDomainSuffix(domains: string[]) {
for (let i = 0, len = domains.length; i < len; i++) {
this.addDomainSuffix(domains[i]);
}
return this;
}
addDomainKeyword(keyword: string) {
this.domainKeywords.add(keyword);
return this;
}
addDomainWildcard(wildcard: string) {
this.domainWildcard.add(wildcard);
return this;
}
private async addFromDomainsetPromise(source: AsyncIterable<string> | Iterable<string> | string[]) {
for await (const line of source) {
if (line[0] === '.') {
this.addDomainSuffix(line);
} else {
this.addDomain(line);
}
}
}
addFromDomainset(source: AsyncIterable<string> | Iterable<string> | string[]) {
this.pendingPromise = this.pendingPromise.then(() => this.addFromDomainsetPromise(source));
return this;
}
async addFromRuleset(source: AsyncIterable<string> | Iterable<string>) {
for await (const line of source) {
const [type, value, arg] = line.split(',');
switch (type) {
case 'DOMAIN':
this.addDomain(value);
break;
case 'DOMAIN-SUFFIX':
this.addDomainSuffix(value);
break;
case 'DOMAIN-KEYWORD':
this.addDomainKeyword(value);
break;
case 'DOMAIN-WILDCARD':
this.addDomainWildcard(value);
break;
case 'IP-CIDR':
(arg === 'no-resolve' ? this.ipcidrNoResolve : this.ipcidr).add(value);
break;
case 'IP-CIDR6':
(arg === 'no-resolve' ? this.ipcidr6NoResolve : this.ipcidr6).add(value);
break;
default:
this.otherRules.add(line);
break;
}
}
return this;
}
bulkAddCIDR4(cidr: string[]) {
for (let i = 0, len = cidr.length; i < len; i++) {
this.ipcidr.add(cidr[i]);
}
return this;
}
bulkAddCIDR6(cidr: string[]) {
for (let i = 0, len = cidr.length; i < len; i++) {
this.ipcidr6.add(cidr[i]);
}
return this;
}
abstract write(): Promise<void>;
}
export class DomainsetOutput extends RuleOutput {
protected type = 'domainset' as const;
async write() {
await this.pendingPromise;
invariant(this.title, 'Missing title');
invariant(this.description, 'Missing description');
const sorted = sortDomains(this.domainTrie.dump(), this.apexDomainMap, this.subDomainMap);
sorted.push('this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
const surge = sorted;
const clash = surgeDomainsetToClashDomainset(sorted);
const singbox = RuleOutput.jsonToLines(surgeDomainsetToSingbox(sorted));
await Promise.all([
compareAndWriteFile(
this.span,
withBannerArray(
this.title,
this.description,
this.date,
surge
),
path.join(OUTPUT_SURGE_DIR, this.type, this.id + '.conf')
),
compareAndWriteFile(
this.span,
withBannerArray(
this.title,
this.description,
this.date,
clash
),
path.join(OUTPUT_CLASH_DIR, this.type, this.id + '.txt')
),
compareAndWriteFile(
this.span,
singbox,
path.join(OUTPUT_SINGBOX_DIR, this.type, this.id + '.json')
)
]);
}
}
export class IPListOutput extends RuleOutput {
protected type = 'ip' as const;
constructor(span: Span, id: string, private readonly clashUseRule = true) {
super(span, id);
}
async write() {
await this.pendingPromise;
invariant(this.title, 'Missing title');
invariant(this.description, 'Missing description');
const sorted4 = Array.from(this.ipcidr);
const sorted6 = Array.from(this.ipcidr6);
const merged = appendArrayInPlace(appendArrayInPlace([], sorted4), sorted6);
const surge = sorted4.map(i => `IP-CIDR,${i}`);
appendArrayInPlace(surge, sorted6.map(i => `IP-CIDR6,${i}`));
surge.push('DOMAIN,this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
const clash = this.clashUseRule ? surge : merged;
const singbox = RuleOutput.jsonToLines(ipCidrListToSingbox(merged));
await Promise.all([
compareAndWriteFile(
this.span,
withBannerArray(
this.title,
this.description,
this.date,
surge
),
path.join(OUTPUT_SURGE_DIR, this.type, this.id + '.conf')
),
compareAndWriteFile(
this.span,
withBannerArray(
this.title,
this.description,
this.date,
clash
),
path.join(OUTPUT_CLASH_DIR, this.type, this.id + '.txt')
),
compareAndWriteFile(
this.span,
singbox,
path.join(OUTPUT_SINGBOX_DIR, this.type, this.id + '.json')
)
]);
}
}

View File

@ -7,7 +7,7 @@ import fs from 'node:fs';
import { fastStringArrayJoin, writeFile } from './misc';
import { readFileByLine } from './fetch-text-by-line';
import stringify from 'json-stringify-pretty-compact';
import { ipCidrListToSingbox, surgeDomainsetToSingbox, surgeRulesetToSingbox } from './singbox';
import { surgeDomainsetToSingbox, surgeRulesetToSingbox } from './singbox';
import { createTrie } from './trie';
import { pack, unpackFirst, unpackSecond } from './bitwise';
import { asyncWriteToStream } from './async-write-to-stream';
@ -23,8 +23,7 @@ export const fileEqual = async (linesA: string[], source: AsyncIterable<string>)
if (index > linesA.length - 1) {
if (index === linesA.length && lineB === '') {
index--;
continue;
return true;
}
// The file becomes smaller
return false;
@ -51,7 +50,7 @@ export const fileEqual = async (linesA: string[], source: AsyncIterable<string>)
}
}
if (index !== linesA.length - 1) {
if (index < linesA.length - 1) {
// The file becomes larger
return false;
}
@ -96,7 +95,7 @@ export async function compareAndWriteFile(span: Span, linesA: string[], filePath
});
}
const withBannerArray = (title: string, description: string[] | readonly string[], date: Date, content: string[]) => {
export const withBannerArray = (title: string, description: string[] | readonly string[], date: Date, content: string[]) => {
return [
'#########################################',
`# ${title}`,
@ -191,7 +190,7 @@ const MARK = 'this_ruleset_is_made_by_sukkaw.ruleset.skk.moe';
export const createRuleset = (
parentSpan: Span,
title: string, description: string[] | readonly string[], date: Date, content: string[],
type: 'ruleset' | 'domainset' | 'ipcidr' | 'ipcidr6',
type: 'ruleset' | 'domainset',
[surgePath, clashPath, singBoxPath, _clashMrsPath]: readonly [
surgePath: string,
clashPath: string,
@ -210,12 +209,6 @@ export const createRuleset = (
case 'ruleset':
_surgeContent = [`DOMAIN,${MARK}`, ...processRuleSet(content)];
break;
case 'ipcidr':
_surgeContent = [`DOMAIN,${MARK}`, ...processRuleSet(content.map(i => `IP-CIDR,${i}`))];
break;
case 'ipcidr6':
_surgeContent = [`DOMAIN,${MARK}`, ...processRuleSet(content.map(i => `IP-CIDR6,${i}`))];
break;
default:
throw new TypeError(`Unknown type: ${type}`);
}
@ -232,10 +225,6 @@ export const createRuleset = (
case 'ruleset':
_clashContent = [`DOMAIN,${MARK}`, ...surgeRulesetToClashClassicalTextRuleset(processRuleSet(content))];
break;
case 'ipcidr':
case 'ipcidr6':
_clashContent = content;
break;
default:
throw new TypeError(`Unknown type: ${type}`);
}
@ -250,10 +239,6 @@ export const createRuleset = (
case 'ruleset':
_singBoxContent = surgeRulesetToSingbox([`DOMAIN,${MARK}`, ...processRuleSet(content)]);
break;
case 'ipcidr':
case 'ipcidr6':
_singBoxContent = ipCidrListToSingbox(content);
break;
default:
throw new TypeError(`Unknown type: ${type}`);
}