From 553dd62eb1f3f42942758f192e4062bb9b2b8331 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Tue, 23 Jul 2024 17:42:10 +0800 Subject: [PATCH] Refactor: make Node.js run compatible --- .swcrc | 4 ++ Build/build-apple-cdn.ts | 10 ++--- Build/build-cdn-download-conf.ts | 16 ++++---- Build/build-chn-cidr.ts | 6 +-- Build/build-cloudmounter-rules.ts | 6 +-- Build/build-common.ts | 8 ++-- Build/build-deprecate-files.ts | 6 +-- ...c-direct-lan-ruleset-dns-mapping-module.ts | 22 +++++------ Build/build-internal-reverse-chn-cidr.ts | 4 +- Build/build-microsoft-cdn.ts | 6 +-- Build/build-public.ts | 6 +-- Build/build-reject-domainset.ts | 16 ++++---- Build/build-reject-ip-list.ts | 8 ++-- Build/build-sgmodule-always-realip.ts | 6 +-- Build/build-sgmodule-redirect.ts | 4 +- Build/build-speedtest-domainset.ts | 8 ++-- Build/build-sspanel-appprofile.ts | 20 +++++----- Build/build-stream-service.ts | 10 ++--- Build/build-telegram-cidr.ts | 6 +-- Build/download-mock-assets.ts | 4 +- Build/download-previous-build.ts | 8 ++-- Build/lib/cache-filesystem.ts | 35 ++++++++--------- Build/lib/create-file.ts | 12 ++---- Build/lib/fetch-text-by-line.bench.ts | 2 +- Build/lib/fetch-text-by-line.ts | 36 +++++++++++++----- Build/lib/get-gorhill-publicsuffix.ts | 10 ++++- Build/trace/index.ts | 4 +- Build/trim-source.ts | 2 +- Build/validate-domestic.ts | 2 +- Build/validate-gfwlist.ts | 6 +-- bun.lockb | Bin 131066 -> 144466 bytes package.json | 6 ++- 32 files changed, 163 insertions(+), 136 deletions(-) diff --git a/.swcrc b/.swcrc index 97a75ad4..65a083f9 100644 --- a/.swcrc +++ b/.swcrc @@ -6,5 +6,9 @@ "syntax": "typescript", "dynamicImport": true } + }, + "module": { + "type": "commonjs", + "ignoreDynamic": true } } diff --git a/Build/build-apple-cdn.ts b/Build/build-apple-cdn.ts index 047ad568..6c084097 100644 --- a/Build/build-apple-cdn.ts +++ b/Build/build-apple-cdn.ts @@ -17,7 +17,7 @@ export const getAppleCdnDomainsPromise = createMemoizedPromise(() => fsFetchCach } )); -export const buildAppleCdn = task(import.meta.main, import.meta.path)(async (span) => { +export const buildAppleCdn = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const res: string[] = await span.traceChildPromise('get apple cdn domains', getAppleCdnDomainsPromise()); const description = [ @@ -40,8 +40,8 @@ export const buildAppleCdn = task(import.meta.main, import.meta.path)(async (spa new Date(), ruleset, 'ruleset', - path.resolve(import.meta.dir, '../List/non_ip/apple_cdn.conf'), - path.resolve(import.meta.dir, '../Clash/non_ip/apple_cdn.txt') + path.resolve(__dirname, '../List/non_ip/apple_cdn.conf'), + path.resolve(__dirname, '../Clash/non_ip/apple_cdn.txt') ), createRuleset( span, @@ -50,8 +50,8 @@ export const buildAppleCdn = task(import.meta.main, import.meta.path)(async (spa new Date(), domainset, 'domainset', - path.resolve(import.meta.dir, '../List/domainset/apple_cdn.conf'), - path.resolve(import.meta.dir, '../Clash/domainset/apple_cdn.txt') + path.resolve(__dirname, '../List/domainset/apple_cdn.conf'), + path.resolve(__dirname, '../Clash/domainset/apple_cdn.txt') ) ]); }); diff --git a/Build/build-cdn-download-conf.ts b/Build/build-cdn-download-conf.ts index 6771dcc9..4ed561bd 100644 --- a/Build/build-cdn-download-conf.ts +++ b/Build/build-cdn-download-conf.ts @@ -48,7 +48,7 @@ const getS3OSSDomainsPromise = (async (): Promise => { return Array.from(S3OSSDomains); })(); -export const buildCdnDownloadConf = task(import.meta.main, import.meta.path)(async (span) => { +export const buildCdnDownloadConf = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const [ S3OSSDomains, @@ -57,9 +57,9 @@ export const buildCdnDownloadConf = task(import.meta.main, import.meta.path)(asy steamDomainSet ] = await Promise.all([ getS3OSSDomainsPromise, - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/domainset/cdn.conf')), - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/domainset/download.conf')), - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/domainset/steam.conf')) + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/cdn.conf')), + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/download.conf')), + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/steam.conf')) ]); appendArrayInPlace(downloadDomainSet, S3OSSDomains.map(domain => `.${domain}`)); @@ -77,8 +77,8 @@ export const buildCdnDownloadConf = task(import.meta.main, import.meta.path)(asy new Date(), sortDomains(domainDeduper(cdnDomainsList)), 'domainset', - path.resolve(import.meta.dir, '../List/domainset/cdn.conf'), - path.resolve(import.meta.dir, '../Clash/domainset/cdn.txt') + path.resolve(__dirname, '../List/domainset/cdn.conf'), + path.resolve(__dirname, '../Clash/domainset/cdn.txt') ), createRuleset( span, @@ -91,8 +91,8 @@ export const buildCdnDownloadConf = task(import.meta.main, import.meta.path)(asy new Date(), sortDomains(domainDeduper(downloadDomainSet)), 'domainset', - path.resolve(import.meta.dir, '../List/domainset/download.conf'), - path.resolve(import.meta.dir, '../Clash/domainset/download.txt') + path.resolve(__dirname, '../List/domainset/download.conf'), + path.resolve(__dirname, '../Clash/domainset/download.txt') ) ]); }); diff --git a/Build/build-chn-cidr.ts b/Build/build-chn-cidr.ts index 834abe13..7484281e 100644 --- a/Build/build-chn-cidr.ts +++ b/Build/build-chn-cidr.ts @@ -16,7 +16,7 @@ export const getChnCidrPromise = createMemoizedPromise(async () => { return exclude(cidr, NON_CN_CIDR_INCLUDED_IN_CHNROUTE, true); }); -export const buildChnCidr = task(import.meta.main, import.meta.path)(async (span) => { +export const buildChnCidr = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const filteredCidr = await span.traceChildAsync('download chnroutes2', getChnCidrPromise); // Can not use SHARED_DESCRIPTION here as different license @@ -38,7 +38,7 @@ export const buildChnCidr = task(import.meta.main, import.meta.path)(async (span new Date(), filteredCidr.map(i => `IP-CIDR,${i}`) ), - pathResolve(import.meta.dir, '../List/ip/china_ip.conf') + pathResolve(__dirname, '../List/ip/china_ip.conf') ), compareAndWriteFile( span, @@ -48,7 +48,7 @@ export const buildChnCidr = task(import.meta.main, import.meta.path)(async (span new Date(), filteredCidr ), - pathResolve(import.meta.dir, '../Clash/ip/china_ip.txt') + pathResolve(__dirname, '../Clash/ip/china_ip.txt') ) ]); }); diff --git a/Build/build-cloudmounter-rules.ts b/Build/build-cloudmounter-rules.ts index 0d953f77..4c22d6a0 100644 --- a/Build/build-cloudmounter-rules.ts +++ b/Build/build-cloudmounter-rules.ts @@ -4,10 +4,10 @@ import { SHARED_DESCRIPTION } from './lib/constants'; import { createRuleset } from './lib/create-file'; import { task } from './trace'; -const outputSurgeDir = path.resolve(import.meta.dir, '../List'); -const outputClashDir = path.resolve(import.meta.dir, '../Clash'); +const outputSurgeDir = path.resolve(__dirname, '../List'); +const outputClashDir = path.resolve(__dirname, '../Clash'); -export const buildCloudMounterRules = task(import.meta.main, import.meta.path)(async (span) => { +export const buildCloudMounterRules = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { // AND,((SRC-IP,192.168.1.110), (DOMAIN, example.com)) const results = DOMAINS.flatMap(domain => { diff --git a/Build/build-common.ts b/Build/build-common.ts index bfacafea..c62446d5 100644 --- a/Build/build-common.ts +++ b/Build/build-common.ts @@ -15,13 +15,13 @@ const MAGIC_COMMAND_SKIP = '# $ custom_build_script'; const MAGIC_COMMAND_TITLE = '# $ meta_title '; const MAGIC_COMMAND_DESCRIPTION = '# $ meta_description '; -const sourceDir = path.resolve(import.meta.dir, '../Source'); -const outputSurgeDir = path.resolve(import.meta.dir, '../List'); -const outputClashDir = path.resolve(import.meta.dir, '../Clash'); +const sourceDir = path.resolve(__dirname, '../Source'); +const outputSurgeDir = path.resolve(__dirname, '../List'); +const outputClashDir = path.resolve(__dirname, '../Clash'); const domainsetSrcFolder = 'domainset' + path.sep; -export const buildCommon = task(import.meta.main, import.meta.path)(async (span) => { +export const buildCommon = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const promises: Array> = []; const paths = await new Fdir() diff --git a/Build/build-deprecate-files.ts b/Build/build-deprecate-files.ts index 2882c861..ec40ce2f 100644 --- a/Build/build-deprecate-files.ts +++ b/Build/build-deprecate-files.ts @@ -8,10 +8,10 @@ const DEPRECATED_FILES = [ ['domainset/reject_phishing', 'This file has been merged with domainset/reject'] ]; -const outputSurgeDir = path.resolve(import.meta.dir, '../List'); -const outputClashDir = path.resolve(import.meta.dir, '../Clash'); +const outputSurgeDir = path.resolve(__dirname, '../List'); +const outputClashDir = path.resolve(__dirname, '../Clash'); -export const buildDeprecateFiles = task(import.meta.main, import.meta.path)((span) => span.traceChildAsync('create deprecated files', async (childSpan) => { +export const buildDeprecateFiles = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)((span) => span.traceChildAsync('create deprecated files', async (childSpan) => { const promises: Array> = []; for (const [filePath, description] of DEPRECATED_FILES) { diff --git a/Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts b/Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts index 96e82918..76da3298 100644 --- a/Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts +++ b/Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts @@ -12,8 +12,8 @@ import * as yaml from 'yaml'; import { appendArrayInPlace } from './lib/append-array-in-place'; export const getDomesticAndDirectDomainsRulesetPromise = createMemoizedPromise(async () => { - const domestics = await readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/non_ip/domestic.conf')); - const directs = await readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/non_ip/direct.conf')); + const domestics = await readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/domestic.conf')); + const directs = await readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/direct.conf')); const lans: string[] = []; Object.entries(DOMESTICS).forEach(([, { domains }]) => { @@ -29,7 +29,7 @@ export const getDomesticAndDirectDomainsRulesetPromise = createMemoizedPromise(a return [domestics, directs, lans] as const; }); -export const buildDomesticRuleset = task(import.meta.main, import.meta.path)(async (span) => { +export const buildDomesticRuleset = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const res = await getDomesticAndDirectDomainsRulesetPromise(); const dataset = Object.entries(DOMESTICS); @@ -48,8 +48,8 @@ export const buildDomesticRuleset = task(import.meta.main, import.meta.path)(asy new Date(), res[0], 'ruleset', - path.resolve(import.meta.dir, '../List/non_ip/domestic.conf'), - path.resolve(import.meta.dir, '../Clash/non_ip/domestic.txt') + path.resolve(__dirname, '../List/non_ip/domestic.conf'), + path.resolve(__dirname, '../Clash/non_ip/domestic.txt') ), createRuleset( span, @@ -62,8 +62,8 @@ export const buildDomesticRuleset = task(import.meta.main, import.meta.path)(asy new Date(), res[1], 'ruleset', - path.resolve(import.meta.dir, '../List/non_ip/direct.conf'), - path.resolve(import.meta.dir, '../Clash/non_ip/direct.txt') + path.resolve(__dirname, '../List/non_ip/direct.conf'), + path.resolve(__dirname, '../Clash/non_ip/direct.txt') ), createRuleset( span, @@ -76,8 +76,8 @@ export const buildDomesticRuleset = task(import.meta.main, import.meta.path)(asy new Date(), res[2], 'ruleset', - path.resolve(import.meta.dir, '../List/non_ip/lan.conf'), - path.resolve(import.meta.dir, '../Clash/non_ip/lan.txt') + path.resolve(__dirname, '../List/non_ip/lan.conf'), + path.resolve(__dirname, '../Clash/non_ip/lan.txt') ), compareAndWriteFile( span, @@ -94,10 +94,10 @@ export const buildDomesticRuleset = task(import.meta.main, import.meta.path)(asy ]) ]) ], - path.resolve(import.meta.dir, '../Modules/sukka_local_dns_mapping.sgmodule') + path.resolve(__dirname, '../Modules/sukka_local_dns_mapping.sgmodule') ), fsp.writeFile( - path.resolve(import.meta.dir, '../Internal/clash_nameserver_policy.yaml'), + path.resolve(__dirname, '../Internal/clash_nameserver_policy.yaml'), yaml.stringify( { dns: { diff --git a/Build/build-internal-reverse-chn-cidr.ts b/Build/build-internal-reverse-chn-cidr.ts index 190c4c68..46d2f96e 100644 --- a/Build/build-internal-reverse-chn-cidr.ts +++ b/Build/build-internal-reverse-chn-cidr.ts @@ -7,7 +7,7 @@ import { NON_CN_CIDR_INCLUDED_IN_CHNROUTE, RESERVED_IPV4_CIDR } from './constant import fsp from 'fs/promises'; -export const buildInternalReverseChnCIDR = task(import.meta.main, import.meta.path)(async () => { +export const buildInternalReverseChnCIDR = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async () => { const cidr = await getChnCidrPromise(); const reversedCidr = merge( @@ -22,7 +22,7 @@ export const buildInternalReverseChnCIDR = task(import.meta.main, import.meta.pa ); return fsp.writeFile( - path.resolve(import.meta.dir, '../Internal/reversed-chn-cidr.txt'), + path.resolve(__dirname, '../Internal/reversed-chn-cidr.txt'), reversedCidr.join('\n') + '\n', { encoding: 'utf-8' } ); diff --git a/Build/build-microsoft-cdn.ts b/Build/build-microsoft-cdn.ts index 50a291b1..48188258 100644 --- a/Build/build-microsoft-cdn.ts +++ b/Build/build-microsoft-cdn.ts @@ -44,7 +44,7 @@ export const getMicrosoftCdnRulesetPromise = createMemoizedPromise(async () => { .concat(WHITELIST); }); -export const buildMicrosoftCdn = task(import.meta.main, import.meta.path)(async (span) => { +export const buildMicrosoftCdn = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const description = [ ...SHARED_DESCRIPTION, '', @@ -63,7 +63,7 @@ export const buildMicrosoftCdn = task(import.meta.main, import.meta.path)(async new Date(), res, 'ruleset', - path.resolve(import.meta.dir, '../List/non_ip/microsoft_cdn.conf'), - path.resolve(import.meta.dir, '../Clash/non_ip/microsoft_cdn.txt') + path.resolve(__dirname, '../List/non_ip/microsoft_cdn.conf'), + path.resolve(__dirname, '../Clash/non_ip/microsoft_cdn.txt') ); }); diff --git a/Build/build-public.ts b/Build/build-public.ts index 10d44f84..23dceec5 100644 --- a/Build/build-public.ts +++ b/Build/build-public.ts @@ -8,8 +8,8 @@ import { sort } from './lib/timsort'; import Trie from 'mnemonist/trie'; -const rootPath = path.resolve(import.meta.dir, '../'); -const publicPath = path.resolve(import.meta.dir, '../public'); +const rootPath = path.resolve(__dirname, '../'); +const publicPath = path.resolve(__dirname, '../public'); const folderAndFilesToBeDeployed = [ `Mock${path.sep}`, @@ -21,7 +21,7 @@ const folderAndFilesToBeDeployed = [ 'LICENSE' ]; -export const buildPublic = task(import.meta.main, import.meta.path)(async (span) => { +export const buildPublic = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { await span .traceChild('copy public files') .traceAsyncFn(async () => { diff --git a/Build/build-reject-domainset.ts b/Build/build-reject-domainset.ts index c230d87b..4b234416 100644 --- a/Build/build-reject-domainset.ts +++ b/Build/build-reject-domainset.ts @@ -20,9 +20,9 @@ import { getPhishingDomains } from './lib/get-phishing-domains'; import { setAddFromArray, setAddFromArrayCurried } from './lib/set-add-from-array'; import { sort } from './lib/timsort'; -const getRejectSukkaConfPromise = readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/domainset/reject_sukka.conf')); +const getRejectSukkaConfPromise = readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/reject_sukka.conf')); -export const buildRejectDomainSet = task(import.meta.main, import.meta.path)(async (span) => { +export const buildRejectDomainSet = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { /** Whitelists */ const filterRuleWhitelistDomainSets = new Set(PREDEFINED_WHITELIST); @@ -98,7 +98,7 @@ export const buildRejectDomainSet = task(import.meta.main, import.meta.path)(asy /** Collect DOMAIN-KEYWORD from non_ip/reject.conf for deduplication */ const domainKeywordsSet = new Set(); - for await (const line of readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/reject.conf'))) { + for await (const line of readFileByLine(path.resolve(__dirname, '../Source/non_ip/reject.conf'))) { const [type, value] = line.split(','); if (type === 'DOMAIN-KEYWORD') { @@ -191,8 +191,8 @@ export const buildRejectDomainSet = task(import.meta.main, import.meta.path)(asy new Date(), span.traceChildSync('sort reject domainset (base)', () => sortDomains(dudupedDominArray, domainArrayMainDomainMap, domainArraySubdomainMap)), 'domainset', - path.resolve(import.meta.dir, '../List/domainset/reject.conf'), - path.resolve(import.meta.dir, '../Clash/domainset/reject.txt') + path.resolve(__dirname, '../List/domainset/reject.conf'), + path.resolve(__dirname, '../Clash/domainset/reject.txt') ), createRuleset( span, @@ -211,13 +211,13 @@ export const buildRejectDomainSet = task(import.meta.main, import.meta.path)(asy new Date(), span.traceChildSync('sort reject domainset (extra)', () => sortDomains(dudupedDominArrayExtra, domainArrayMainDomainMap, domainArraySubdomainMap)), 'domainset', - path.resolve(import.meta.dir, '../List/domainset/reject_extra.conf'), - path.resolve(import.meta.dir, '../Clash/domainset/reject_extra.txt') + path.resolve(__dirname, '../List/domainset/reject_extra.conf'), + path.resolve(__dirname, '../Clash/domainset/reject_extra.txt') ), compareAndWriteFile( span, rejectDomainsStats.map(([domain, count]) => `${domain}${' '.repeat(100 - domain.length)}${count}`), - path.resolve(import.meta.dir, '../Internal/reject-stats.txt') + path.resolve(__dirname, '../Internal/reject-stats.txt') ) ]); }); diff --git a/Build/build-reject-ip-list.ts b/Build/build-reject-ip-list.ts index 972c2678..1a4ceac6 100644 --- a/Build/build-reject-ip-list.ts +++ b/Build/build-reject-ip-list.ts @@ -65,9 +65,9 @@ const getBotNetFilterIPsPromise = fsFetchCache.apply( } ); -const localRejectIPSourcesPromise = readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/ip/reject.conf')); +const localRejectIPSourcesPromise = readFileIntoProcessedArray(path.resolve(__dirname, '../Source/ip/reject.conf')); -export const buildRejectIPList = task(import.meta.main, import.meta.path)(async (span) => { +export const buildRejectIPList = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const result = await localRejectIPSourcesPromise; const bogusNxDomainIPs = await span.traceChildPromise('get bogus nxdomain ips', getBogusNxDomainIPsPromise); @@ -93,7 +93,7 @@ export const buildRejectIPList = task(import.meta.main, import.meta.path)(async new Date(), result, 'ruleset', - path.resolve(import.meta.dir, '../List/ip/reject.conf'), - path.resolve(import.meta.dir, '../Clash/ip/reject.txt') + path.resolve(__dirname, '../List/ip/reject.conf'), + path.resolve(__dirname, '../Clash/ip/reject.txt') ); }); diff --git a/Build/build-sgmodule-always-realip.ts b/Build/build-sgmodule-always-realip.ts index 333e7ef3..f12e64ea 100644 --- a/Build/build-sgmodule-always-realip.ts +++ b/Build/build-sgmodule-always-realip.ts @@ -43,7 +43,7 @@ const HOSTNAMES = [ '*.battlenet.com' ]; -export const buildAlwaysRealIPModule = task(import.meta.main, import.meta.path)(async (span) => { +export const buildAlwaysRealIPModule = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { // Intranet, Router Setup, and mant more const dataset = [Object.entries(DIRECTS), Object.entries(LANS)]; const surge = dataset.flatMap(data => data.flatMap(([, { domains }]) => domains.flatMap((domain) => [`*.${domain}`, domain]))); @@ -59,10 +59,10 @@ export const buildAlwaysRealIPModule = task(import.meta.main, import.meta.path)( '[General]', `always-real-ip = %APPEND% ${HOSTNAMES.concat(surge).join(', ')}` ], - path.resolve(import.meta.dir, '../Modules/sukka_common_always_realip.sgmodule') + path.resolve(__dirname, '../Modules/sukka_common_always_realip.sgmodule') ), fsp.writeFile( - path.resolve(import.meta.dir, '../Internal/clash_fake_ip_filter.yaml'), + path.resolve(__dirname, '../Internal/clash_fake_ip_filter.yaml'), yaml.stringify( { dns: { diff --git a/Build/build-sgmodule-redirect.ts b/Build/build-sgmodule-redirect.ts index cd98c651..592bf4c8 100644 --- a/Build/build-sgmodule-redirect.ts +++ b/Build/build-sgmodule-redirect.ts @@ -120,7 +120,7 @@ const REDIRECT_FAKEWEBSITES = [ ['zbrushcn.com', 'https://www.maxon.net/en/zbrush'] ] as const; -export const buildRedirectModule = task(import.meta.main, import.meta.path)(async (span) => { +export const buildRedirectModule = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const domains = Array.from(new Set([ ...REDIRECT_MIRROR.map(([from]) => getHostname(from, { detectIp: false })), ...REDIRECT_FAKEWEBSITES.flatMap(([from]) => [from, `www.${from}`]) @@ -139,6 +139,6 @@ export const buildRedirectModule = task(import.meta.main, import.meta.path)(asyn ...REDIRECT_MIRROR.map(([from, to]) => `^https?://${escapeRegExp(from)}(.*) ${to}$1`), ...REDIRECT_FAKEWEBSITES.map(([from, to]) => `^https?://(www.)?${escapeRegExp(from)} ${to}`) ], - path.resolve(import.meta.dir, '../Modules/sukka_url_redirect.sgmodule') + path.resolve(__dirname, '../Modules/sukka_url_redirect.sgmodule') ); }); diff --git a/Build/build-speedtest-domainset.ts b/Build/build-speedtest-domainset.ts index 5079f8f1..ae6828fb 100644 --- a/Build/build-speedtest-domainset.ts +++ b/Build/build-speedtest-domainset.ts @@ -82,7 +82,7 @@ const querySpeedtestApi = async (keyword: string): Promise> } }; -export const buildSpeedtestDomainSet = task(import.meta.main, import.meta.path)(async (span) => { +export const buildSpeedtestDomainSet = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const domainTrie = createTrie( [ // speedtest.net @@ -183,7 +183,7 @@ export const buildSpeedtestDomainSet = task(import.meta.main, import.meta.path)( async () => { try { ( - await readFileIntoProcessedArray(path.resolve(import.meta.dir, '../List/domainset/speedtest.conf')) + await readFileIntoProcessedArray(path.resolve(__dirname, '../List/domainset/speedtest.conf')) ) .forEach(line => { const hn = getHostname(line, { detectIp: false, validateHostname: true }); if (hn) { @@ -267,7 +267,7 @@ export const buildSpeedtestDomainSet = task(import.meta.main, import.meta.path)( new Date(), deduped, 'domainset', - path.resolve(import.meta.dir, '../List/domainset/speedtest.conf'), - path.resolve(import.meta.dir, '../Clash/domainset/speedtest.txt') + path.resolve(__dirname, '../List/domainset/speedtest.conf'), + path.resolve(__dirname, '../Clash/domainset/speedtest.txt') ); }); diff --git a/Build/build-sspanel-appprofile.ts b/Build/build-sspanel-appprofile.ts index 97ce6626..2cfda001 100644 --- a/Build/build-sspanel-appprofile.ts +++ b/Build/build-sspanel-appprofile.ts @@ -28,7 +28,7 @@ const removeNoResolved = (line: string) => line.replace(',no-resolve', ''); /** * This only generates a simplified version, for under-used users only. */ -export const buildSSPanelUIMAppProfile = task(import.meta.main, import.meta.path)(async (span) => { +export const buildSSPanelUIMAppProfile = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const [ [domesticDomains, directDomains, lanDomains], appleCdnDomains, @@ -55,18 +55,18 @@ export const buildSSPanelUIMAppProfile = task(import.meta.main, import.meta.path ), getAppleCdnDomainsPromise().then(domains => domains.map(domain => `DOMAIN-SUFFIX,${domain}`)), getMicrosoftCdnRulesetPromise().then(surgeRulesetToClashClassicalTextRuleset), - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/non_ip/apple_cn.conf')), - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/non_ip/neteasemusic.conf')).then(surgeRulesetToClashClassicalTextRuleset), + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/apple_cn.conf')), + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/neteasemusic.conf')).then(surgeRulesetToClashClassicalTextRuleset), // microsoft & apple - domains - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/non_ip/microsoft.conf')), - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/non_ip/apple_services.conf')).then(surgeRulesetToClashClassicalTextRuleset), + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/microsoft.conf')), + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/apple_services.conf')).then(surgeRulesetToClashClassicalTextRuleset), // stream - domains surgeRulesetToClashClassicalTextRuleset(AllStreamServices.flatMap((i) => i.rules)), // steam - domains - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/domainset/steam.conf')).then(surgeDomainsetToClashRuleset), + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/steam.conf')).then(surgeDomainsetToClashRuleset), // global - domains - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/non_ip/global.conf')).then(surgeRulesetToClashClassicalTextRuleset), - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/non_ip/telegram.conf')).then(surgeRulesetToClashClassicalTextRuleset), + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/global.conf')).then(surgeRulesetToClashClassicalTextRuleset), + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/telegram.conf')).then(surgeRulesetToClashClassicalTextRuleset), // domestic - ip cidr getChnCidrPromise().then(cidrs => cidrs.map(cidr => `IP-CIDR,${cidr}`)), AllStreamServices.flatMap((i) => ( @@ -80,7 +80,7 @@ export const buildSSPanelUIMAppProfile = task(import.meta.main, import.meta.path // global - ip cidr getTelegramCIDRPromise(), // lan - ip cidr - readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/ip/lan.conf')) + readFileIntoProcessedArray(path.resolve(__dirname, '../Source/ip/lan.conf')) ] as const); const telegramCidrs = rawTelegramCidrs.map(removeNoResolved); @@ -118,7 +118,7 @@ export const buildSSPanelUIMAppProfile = task(import.meta.main, import.meta.path await compareAndWriteFile( span, output, - path.resolve(import.meta.dir, '../Internal/appprofile.php') + path.resolve(__dirname, '../Internal/appprofile.php') ); }); diff --git a/Build/build-stream-service.ts b/Build/build-stream-service.ts index b4eff845..f0682458 100644 --- a/Build/build-stream-service.ts +++ b/Build/build-stream-service.ts @@ -22,8 +22,8 @@ export const createRulesetForStreamService = (span: Span, fileId: string, title: new Date(), streamServices.flatMap((i) => i.rules), 'ruleset', - path.resolve(import.meta.dir, `../List/non_ip/${fileId}.conf`), - path.resolve(import.meta.dir, `../Clash/non_ip/${fileId}.txt`) + path.resolve(__dirname, `../List/non_ip/${fileId}.conf`), + path.resolve(__dirname, `../Clash/non_ip/${fileId}.txt`) ), // IP createRuleset( @@ -44,13 +44,13 @@ export const createRulesetForStreamService = (span: Span, fileId: string, title: : [] )), 'ruleset', - path.resolve(import.meta.dir, `../List/ip/${fileId}.conf`), - path.resolve(import.meta.dir, `../Clash/ip/${fileId}.txt`) + path.resolve(__dirname, `../List/ip/${fileId}.conf`), + path.resolve(__dirname, `../Clash/ip/${fileId}.txt`) ) ])); }; -export const buildStreamService = task(import.meta.main, import.meta.path)(async (span) => { +export const buildStreamService = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { return Promise.all([ createRulesetForStreamService(span, 'stream', 'All', ALL), createRulesetForStreamService(span, 'stream_us', 'North America', NORTH_AMERICA), diff --git a/Build/build-telegram-cidr.ts b/Build/build-telegram-cidr.ts index 1ac9a5ec..3eaa2576 100644 --- a/Build/build-telegram-cidr.ts +++ b/Build/build-telegram-cidr.ts @@ -32,7 +32,7 @@ export const getTelegramCIDRPromise = createMemoizedPromise(async () => { return { date, results }; }); -export const buildTelegramCIDR = task(import.meta.main, import.meta.path)(async (span) => { +export const buildTelegramCIDR = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const { date, results } = await span.traceChildAsync('get telegram cidr', getTelegramCIDRPromise); if (results.length === 0) { @@ -52,7 +52,7 @@ export const buildTelegramCIDR = task(import.meta.main, import.meta.path)(async date, results, 'ruleset', - path.resolve(import.meta.dir, '../List/ip/telegram.conf'), - path.resolve(import.meta.dir, '../Clash/ip/telegram.txt') + path.resolve(__dirname, '../List/ip/telegram.conf'), + path.resolve(__dirname, '../Clash/ip/telegram.txt') ); }); diff --git a/Build/download-mock-assets.ts b/Build/download-mock-assets.ts index 6a729d1f..06bc79f4 100644 --- a/Build/download-mock-assets.ts +++ b/Build/download-mock-assets.ts @@ -13,9 +13,9 @@ const ASSETS_LIST = { 'amazon-adsystem-com_amazon-apstag.js': 'https://raw.githubusercontent.com/AdguardTeam/Scriptlets/master/dist/redirect-files/amazon-apstag.js' } as const; -const mockDir = path.resolve(import.meta.dir, '../Mock'); +const mockDir = path.resolve(__dirname, '../Mock'); -export const downloadMockAssets = task(import.meta.main, import.meta.path)((span) => Promise.all(Object.entries(ASSETS_LIST).map( +export const downloadMockAssets = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)((span) => Promise.all(Object.entries(ASSETS_LIST).map( ([filename, url]) => span .traceChild(url) .traceAsyncFn(() => fetchWithRetry(url).then(res => { diff --git a/Build/download-previous-build.ts b/Build/download-previous-build.ts index dd946fb7..31467e38 100644 --- a/Build/download-previous-build.ts +++ b/Build/download-previous-build.ts @@ -13,7 +13,7 @@ import { Readable } from 'stream'; const IS_READING_BUILD_OUTPUT = 1 << 2; const ALL_FILES_EXISTS = 1 << 3; -export const downloadPreviousBuild = task(import.meta.main, import.meta.path)(async (span) => { +export const downloadPreviousBuild = task(typeof Bun !== 'undefined' ? Bun.main === __filename : require.main === module, __filename)(async (span) => { const buildOutputList: string[] = []; let flag = 1 | ALL_FILES_EXISTS; @@ -21,7 +21,7 @@ export const downloadPreviousBuild = task(import.meta.main, import.meta.path)(as await span .traceChild('read .gitignore') .traceAsyncFn(async () => { - for await (const line of readFileByLine(path.resolve(import.meta.dir, '../.gitignore'))) { + for await (const line of readFileByLine(path.resolve(__dirname, '../.gitignore'))) { if (line === '# $ build output') { flag = flag | IS_READING_BUILD_OUTPUT; continue; @@ -33,7 +33,7 @@ export const downloadPreviousBuild = task(import.meta.main, import.meta.path)(as buildOutputList.push(line); if (!isCI) { - if (!existsSync(path.join(import.meta.dir, '..', line))) { + if (!existsSync(path.join(__dirname, '..', line))) { flag = flag & ~ALL_FILES_EXISTS; } } @@ -83,7 +83,7 @@ export const downloadPreviousBuild = task(import.meta.main, import.meta.path)(as } const relativeEntryPath = entry.header.name.replace(pathPrefix, ''); - const targetPath = path.join(import.meta.dir, '..', relativeEntryPath); + const targetPath = path.join(__dirname, '..', relativeEntryPath); await mkdir(path.dirname(targetPath), { recursive: true }); await pipeline(entry, createWriteStream(targetPath)); diff --git a/Build/lib/cache-filesystem.ts b/Build/lib/cache-filesystem.ts index 40b6ccb9..db74cc9a 100644 --- a/Build/lib/cache-filesystem.ts +++ b/Build/lib/cache-filesystem.ts @@ -1,11 +1,11 @@ -// eslint-disable-next-line import-x/no-unresolved -- bun built-in module -import { Database } from 'bun:sqlite'; +import createDb from 'better-sqlite3'; +import type { Database } from 'better-sqlite3'; import os from 'os'; import path from 'path'; import { mkdirSync } from 'fs'; import picocolors from 'picocolors'; import { fastStringArrayJoin } from './misc'; -import { peek } from 'bun'; +import { peek } from './bun'; import { performance } from 'perf_hooks'; const identity = (x: any) => x; @@ -92,12 +92,12 @@ export class Cache { this.type = 'string'; } - const db = new Database(path.join(this.cachePath, 'cache.db')); + const db = createDb(path.join(this.cachePath, 'cache.db')); - db.exec('PRAGMA journal_mode = WAL;'); - db.exec('PRAGMA synchronous = normal;'); - db.exec('PRAGMA temp_store = memory;'); - db.exec('PRAGMA optimize;'); + db.pragma('journal_mode = WAL'); + db.pragma('synchronous = normal'); + db.pragma('temp_store = memory'); + db.pragma('optimize'); db.prepare(`CREATE TABLE IF NOT EXISTS ${this.tableName} (key TEXT PRIMARY KEY, value ${this.type === 'string' ? 'TEXT' : 'BLOB'}, ttl REAL NOT NULL);`).run(); db.prepare(`CREATE INDEX IF NOT EXISTS cache_ttl ON ${this.tableName} (ttl);`).run(); @@ -130,15 +130,20 @@ export class Cache { `INSERT INTO ${this.tableName} (key, value, ttl) VALUES ($key, $value, $valid) ON CONFLICT(key) DO UPDATE SET value = $value, ttl = $valid` ); + const valid = Date.now() + ttl; + insert.run({ $key: key, + key, $value: value, - $valid: Date.now() + ttl + value, + $valid: valid, + valid }); } get(key: string, defaultValue?: S): S | undefined { - const rv = this.db.prepare<{ value: S }, string>( + const rv = this.db.prepare( `SELECT value FROM ${this.tableName} WHERE key = ? LIMIT 1` ).get(key); @@ -148,7 +153,7 @@ export class Cache { has(key: string): CacheStatus { const now = Date.now(); - const rv = this.db.prepare<{ ttl: number }, string>(`SELECT ttl FROM ${this.tableName} WHERE key = ?`).get(key); + const rv = this.db.prepare(`SELECT ttl FROM ${this.tableName} WHERE key = ?`).get(key); return !rv ? CacheStatus.Miss : (rv.ttl > now ? CacheStatus.Hit : CacheStatus.Stale); } @@ -206,18 +211,14 @@ export class Cache { } } -export const fsFetchCache = new Cache({ cachePath: path.resolve(import.meta.dir, '../../.cache') }); +export const fsFetchCache = new Cache({ cachePath: path.resolve(__dirname, '../../.cache') }); // process.on('exit', () => { // fsFetchCache.destroy(); // }); -// export const fsCache = traceSync('initializing filesystem cache', () => new Cache({ cachePath: path.resolve(import.meta.dir, '../../.cache'), type: 'buffer' })); +// export const fsCache = traceSync('initializing filesystem cache', () => new Cache({ cachePath: path.resolve(__dirname, '../../.cache'), type: 'buffer' })); const separator = '\u0000'; -// const textEncoder = new TextEncoder(); -// const textDecoder = new TextDecoder(); -// export const serializeString = (str: string) => textEncoder.encode(str); -// export const deserializeString = (str: string) => textDecoder.decode(new Uint8Array(str.split(separator).map(Number))); export const serializeSet = (set: Set) => fastStringArrayJoin(Array.from(set), separator); export const deserializeSet = (str: string) => new Set(str.split(separator)); export const serializeArray = (arr: string[]) => fastStringArrayJoin(arr, separator); diff --git a/Build/lib/create-file.ts b/Build/lib/create-file.ts index d9a76b07..662e8910 100644 --- a/Build/lib/create-file.ts +++ b/Build/lib/create-file.ts @@ -11,8 +11,6 @@ import { readFileByLine } from './fetch-text-by-line'; export async function compareAndWriteFile(span: Span, linesA: string[], filePath: string) { let isEqual = true; - const fd = await fsp.open(filePath); - const linesALen = linesA.length; if (!fs.existsSync(filePath)) { @@ -22,12 +20,10 @@ export async function compareAndWriteFile(span: Span, linesA: string[], filePath console.log(`Nothing to write to ${filePath}...`); isEqual = false; } else { - /* The `isEqual` variable is used to determine whether the content of a file is equal to the - provided lines or not. It is initially set to `true`, and then it is updated based on different - conditions: */ + const fd = await fsp.open(filePath); + isEqual = await span.traceChildAsync(`comparing ${filePath}`, async () => { let index = 0; - for await (const lineB of readFileByLine(fd)) { const lineA = linesA[index] as string | undefined; index++; @@ -63,6 +59,8 @@ export async function compareAndWriteFile(span: Span, linesA: string[], filePath return true; }); + + await fd.close(); } if (isEqual) { @@ -83,8 +81,6 @@ export async function compareAndWriteFile(span: Span, linesA: string[], filePath // return writer.end(); }); - - await fd.close(); } export const withBannerArray = (title: string, description: string[] | readonly string[], date: Date, content: string[]) => { diff --git a/Build/lib/fetch-text-by-line.bench.ts b/Build/lib/fetch-text-by-line.bench.ts index 6093d7e3..8159a4a9 100644 --- a/Build/lib/fetch-text-by-line.bench.ts +++ b/Build/lib/fetch-text-by-line.bench.ts @@ -4,7 +4,7 @@ import { readFileByLine } from './fetch-text-by-line'; import path from 'path'; import fsp from 'fs/promises'; -const file = path.resolve(import.meta.dir, '../../Source/domainset/cdn.conf'); +const file = path.resolve(__dirname, '../../Source/domainset/cdn.conf'); group('read file by line', () => { bench('readline', () => processLineFromReadline(readFileByLine(file))); diff --git a/Build/lib/fetch-text-by-line.ts b/Build/lib/fetch-text-by-line.ts index 3e89f448..c69b6064 100644 --- a/Build/lib/fetch-text-by-line.ts +++ b/Build/lib/fetch-text-by-line.ts @@ -1,9 +1,12 @@ +import fs from 'fs'; +import { Readable } from 'stream'; import type { BunFile } from 'bun'; import { fetchWithRetry, defaultRequestInit } from './fetch-retry'; import type { FileHandle } from 'fs/promises'; import { TextLineStream } from './text-line-transform-stream'; import { PolyfillTextDecoderStream } from './text-decoder-stream'; +import { TextDecoderStream as NodeTextDecoderStream } from 'stream/web'; import { processLine } from './process-line'; const enableTextLineStream = !!process.env.ENABLE_TEXT_LINE_STREAM; @@ -36,19 +39,32 @@ async function *createTextLineAsyncIterableFromStreamSource(stream: ReadableStre } } -const getReadableStream = (file: string | BunFile | FileHandle): ReadableStream => { - if (typeof file === 'string') { - return Bun.file(file).stream(); +const getReadableStream = typeof Bun !== 'undefined' + ? (file: string | BunFile | FileHandle): ReadableStream => { + if (typeof file === 'string') { + return Bun.file(file).stream(); + } + if ('writer' in file) { + return file.stream(); + } + return file.readableWebStream(); } - if ('writer' in file) { - return file.stream(); - } - return file.readableWebStream(); -}; + : (file: string | BunFile | FileHandle): ReadableStream => { + if (typeof file === 'string') { + return Readable.toWeb(fs.createReadStream(file /* { encoding: 'utf-8' } */)); + } + if ('writer' in file) { + return file.stream(); + } + return file.readableWebStream(); + }; + +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- On Bun, NodeTextDecoderStream is undefined +const TextDecoderStream = NodeTextDecoderStream ?? PolyfillTextDecoderStream; // TODO: use FileHandle.readLine() export const readFileByLine: ((file: string | BunFile | FileHandle) => AsyncIterable) = enableTextLineStream - ? (file: string | BunFile | FileHandle) => getReadableStream(file).pipeThrough(new PolyfillTextDecoderStream()).pipeThrough(new TextLineStream()) + ? (file: string | BunFile | FileHandle) => getReadableStream(file).pipeThrough(new TextDecoderStream()).pipeThrough(new TextLineStream()) : (file: string | BunFile | FileHandle) => createTextLineAsyncIterableFromStreamSource(getReadableStream(file)); const ensureResponseBody = (resp: Response) => { @@ -62,7 +78,7 @@ const ensureResponseBody = (resp: Response) => { }; export const createReadlineInterfaceFromResponse: ((resp: Response) => AsyncIterable) = enableTextLineStream - ? (resp) => ensureResponseBody(resp).pipeThrough(new PolyfillTextDecoderStream()).pipeThrough(new TextLineStream()) + ? (resp) => ensureResponseBody(resp).pipeThrough(new TextDecoderStream()).pipeThrough(new TextLineStream()) : (resp) => createTextLineAsyncIterableFromStreamSource(ensureResponseBody(resp)); export function fetchRemoteTextByLine(url: string | URL) { diff --git a/Build/lib/get-gorhill-publicsuffix.ts b/Build/lib/get-gorhill-publicsuffix.ts index 78766707..503f30af 100644 --- a/Build/lib/get-gorhill-publicsuffix.ts +++ b/Build/lib/get-gorhill-publicsuffix.ts @@ -1,10 +1,16 @@ -import { toASCII } from 'punycode'; +import fsp from 'fs/promises'; +import { toASCII } from 'punycode/punycode'; import { createMemoizedPromise } from './memo-promise'; import { getPublicSuffixListTextPromise } from './download-publicsuffixlist'; +import { fileURLToPath } from 'url'; const customFetch = typeof Bun !== 'undefined' ? (url: string | URL) => Promise.resolve(Bun.file(url)) - : (url: string | URL) => fetch(url).then(resp => resp.blob() as Promise); + : async (url: string | URL) => { + const filePath = fileURLToPath(url); + const file = await fsp.readFile(filePath); + return new Blob([file]) as any; + }; export const getGorhillPublicSuffixPromise = createMemoizedPromise(async () => { const [publicSuffixListDat, { default: gorhill }] = await Promise.all([ diff --git a/Build/trace/index.ts b/Build/trace/index.ts index dac69c49..bc223993 100644 --- a/Build/trace/index.ts +++ b/Build/trace/index.ts @@ -1,4 +1,4 @@ -import path from 'path'; +import { basename, extname } from 'path'; import picocolors from 'picocolors'; const SPAN_STATUS_START = 0; @@ -95,7 +95,7 @@ export const createSpan = (name: string, parentTraceResult?: TraceResult): Span }; export const task = (importMetaMain: boolean, importMetaPath: string) => (fn: (span: Span) => Promise, customName?: string) => { - const taskName = customName ?? path.basename(importMetaPath, path.extname(importMetaPath)); + const taskName = customName ?? basename(importMetaPath, extname(importMetaPath)); const dummySpan = createSpan(taskName); diff --git a/Build/trim-source.ts b/Build/trim-source.ts index 58d48a93..30840c2a 100644 --- a/Build/trim-source.ts +++ b/Build/trim-source.ts @@ -3,7 +3,7 @@ import fsp from 'fs/promises'; import { fdir as Fdir } from 'fdir'; import { readFileByLine } from './lib/fetch-text-by-line'; -const sourceDir = path.resolve(import.meta.dir, '../Source'); +const sourceDir = path.resolve(__dirname, '../Source'); (async () => { const promises: Array> = []; diff --git a/Build/validate-domestic.ts b/Build/validate-domestic.ts index 7ce7bbc4..52992496 100644 --- a/Build/validate-domestic.ts +++ b/Build/validate-domestic.ts @@ -62,7 +62,7 @@ export const parseDomesticList = async () => { }; // await Promise.all([ - await runAgainstRuleset(path.resolve(import.meta.dir, '../List/non_ip/domestic.conf')); + await runAgainstRuleset(path.resolve(__dirname, '../List/non_ip/domestic.conf')); // ]); console.log(notIncludedDomestic.size, notIncludedDomestic); diff --git a/Build/validate-gfwlist.ts b/Build/validate-gfwlist.ts index aa84fba5..7ee4e8d3 100644 --- a/Build/validate-gfwlist.ts +++ b/Build/validate-gfwlist.ts @@ -105,9 +105,9 @@ export const parseGfwList = async () => { }; await Promise.all([ - runAgainstRuleset(path.resolve(import.meta.dir, '../Source/non_ip/global.conf')), - runAgainstRuleset(path.resolve(import.meta.dir, '../Source/non_ip/telegram.conf')), - runAgainstRuleset(path.resolve(import.meta.dir, '../List/non_ip/stream.conf')) + runAgainstRuleset(path.resolve(__dirname, '../Source/non_ip/global.conf')), + runAgainstRuleset(path.resolve(__dirname, '../Source/non_ip/telegram.conf')), + runAgainstRuleset(path.resolve(__dirname, '../List/non_ip/stream.conf')) ]); console.log(notIncludedTop500Gfwed); diff --git a/bun.lockb b/bun.lockb index c6173197bfdd71a902f75bdcef6e69a8267ff158..633bba7d3b5a8cbaab147d5a7326fd673bfbefa3 100755 GIT binary patch delta 32989 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 Kp{6y}-}^uIKo|}H diff --git a/package.json b/package.json index 8cdc8a2f..e8bd9661 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "scripts": { "build": "bun ./Build/index.ts", - "build-node": "ENABLE_TEXT_LINE_STREAM=true node -r @swc-node/register ./Build/index.ts", + "build-node": "SWCRC=true ENABLE_TEXT_LINE_STREAM=true node -r @swc-node/register ./Build/index.ts", "build-stream": "ENABLE_TEXT_LINE_STREAM=true bun ./Build/index.ts", "lint": "eslint --format=sukka ." }, @@ -20,6 +20,7 @@ "@gorhill/publicsuffixlist": "3.0.1", "async-retry": "^1.3.3", "async-sema": "^3.1.1", + "better-sqlite3": "^11.1.2", "ci-info": "^4.0.0", "csv-parse": "^5.5.6", "fast-cidr-tools": "^0.2.5", @@ -38,8 +39,11 @@ "devDependencies": { "@eslint-sukka/node": "^6.1.6", "@swc-node/register": "^1.10.9", + "@swc/core": "^1.7.0", "@types/async-retry": "^1.4.8", + "@types/better-sqlite3": "^7.6.11", "@types/bun": "^1.1.6", + "@types/punycode": "^2.1.4", "@types/tar-stream": "^3.1.3", "bun-types": "^1.1.20", "eslint": "^9.7.0",