Chore: refactor output dir

This commit is contained in:
SukkaW 2024-09-06 19:49:17 +08:00
parent 5bc2e017df
commit 315b38b999
19 changed files with 117 additions and 172 deletions

14
.gitignore vendored
View File

@ -5,17 +5,3 @@ node_modules
.cache .cache
public public
tmp* tmp*
# $ build output
List/
Clash/
Internal/
sing-box/
Modules/sukka_local_dns_mapping.sgmodule
Modules/sukka_url_redirect.sgmodule
Modules/sukka_common_always_realip.sgmodule
Mock/www-google-analytics-com_ga.js
Mock/www-googletagservices-com_gpt.js
Mock/www-google-analytics-com_analytics.js
Mock/www-googlesyndication-com_adsbygoogle.js
Mock/amazon-adsystem-com_amazon-apstag.js

View File

@ -9,6 +9,7 @@ import { domainDeduper } from './lib/domain-deduper';
import { appendArrayInPlace } from './lib/append-array-in-place'; import { appendArrayInPlace } from './lib/append-array-in-place';
import { sortDomains } from './lib/stable-sort-domain'; import { sortDomains } from './lib/stable-sort-domain';
import { output } from './lib/misc'; import { output } from './lib/misc';
import { SOURCE_DIR } from './constants/dir';
const getS3OSSDomainsPromise = (async (): Promise<string[]> => { const getS3OSSDomainsPromise = (async (): Promise<string[]> => {
const trie = createTrie( const trie = createTrie(
@ -58,9 +59,9 @@ export const buildCdnDownloadConf = task(require.main === module, __filename)(as
steamDomainSet steamDomainSet
] = await Promise.all([ ] = await Promise.all([
getS3OSSDomainsPromise, getS3OSSDomainsPromise,
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/cdn.conf')), readFileIntoProcessedArray(path.join(SOURCE_DIR, 'domainset/cdn.conf')),
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/download.conf')), readFileIntoProcessedArray(path.join(SOURCE_DIR, 'domainset/download.conf')),
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/steam.conf')) readFileIntoProcessedArray(path.join(SOURCE_DIR, 'domainset/steam.conf'))
]); ]);
appendArrayInPlace(downloadDomainSet, S3OSSDomains.map(domain => `.${domain}`)); appendArrayInPlace(downloadDomainSet, S3OSSDomains.map(domain => `.${domain}`));

View File

@ -11,17 +11,13 @@ import { SHARED_DESCRIPTION } from './lib/constants';
import { fdir as Fdir } from 'fdir'; import { fdir as Fdir } from 'fdir';
import { appendArrayInPlace } from './lib/append-array-in-place'; import { appendArrayInPlace } from './lib/append-array-in-place';
import { removeFiles } from './lib/misc'; import { removeFiles } from './lib/misc';
import { OUTPUT_CLASH_DIR, OUTPUT_SINGBOX_DIR, OUTPUT_SURGE_DIR, SOURCE_DIR } from './constants/dir';
const MAGIC_COMMAND_SKIP = '# $ custom_build_script'; const MAGIC_COMMAND_SKIP = '# $ custom_build_script';
const MAGIC_COMMAND_RM = '# $ custom_no_output'; const MAGIC_COMMAND_RM = '# $ custom_no_output';
const MAGIC_COMMAND_TITLE = '# $ meta_title '; const MAGIC_COMMAND_TITLE = '# $ meta_title ';
const MAGIC_COMMAND_DESCRIPTION = '# $ meta_description '; const MAGIC_COMMAND_DESCRIPTION = '# $ meta_description ';
const sourceDir = path.resolve(__dirname, '../Source');
const outputSurgeDir = path.resolve(__dirname, '../List');
const outputClashDir = path.resolve(__dirname, '../Clash');
const outputSingboxDir = path.resolve(__dirname, '../sing-box');
const domainsetSrcFolder = 'domainset' + path.sep; const domainsetSrcFolder = 'domainset' + path.sep;
export const buildCommon = task(require.main === module, __filename)(async (span) => { export const buildCommon = task(require.main === module, __filename)(async (span) => {
@ -46,12 +42,12 @@ export const buildCommon = task(require.main === module, __filename)(async (span
return true; return true;
}) })
.crawl(sourceDir) .crawl(SOURCE_DIR)
.withPromise(); .withPromise();
for (let i = 0, len = paths.length; i < len; i++) { for (let i = 0, len = paths.length; i < len; i++) {
const relativePath = paths[i]; const relativePath = paths[i];
const fullPath = sourceDir + path.sep + relativePath; const fullPath = SOURCE_DIR + path.sep + relativePath;
if (relativePath.startsWith(domainsetSrcFolder)) { if (relativePath.startsWith(domainsetSrcFolder)) {
promises.push(transformDomainset(span, fullPath, relativePath)); promises.push(transformDomainset(span, fullPath, relativePath));
@ -127,9 +123,9 @@ function transformDomainset(parentSpan: Span, sourcePath: string, relativePath:
if (res === $rm) { if (res === $rm) {
return removeFiles([ return removeFiles([
path.resolve(outputSurgeDir, relativePath), path.resolve(OUTPUT_SURGE_DIR, relativePath),
path.resolve(outputClashDir, `${clashFileBasename}.txt`), path.resolve(OUTPUT_CLASH_DIR, `${clashFileBasename}.txt`),
path.resolve(outputSingboxDir, `${clashFileBasename}.json`) path.resolve(OUTPUT_SINGBOX_DIR, `${clashFileBasename}.json`)
]); ]);
} }
@ -153,9 +149,9 @@ function transformDomainset(parentSpan: Span, sourcePath: string, relativePath:
deduped, deduped,
'domainset', 'domainset',
[ [
path.resolve(outputSurgeDir, relativePath), path.resolve(OUTPUT_SURGE_DIR, relativePath),
path.resolve(outputClashDir, `${clashFileBasename}.txt`), path.resolve(OUTPUT_CLASH_DIR, `${clashFileBasename}.txt`),
path.resolve(outputSingboxDir, `${clashFileBasename}.json`) path.resolve(OUTPUT_SINGBOX_DIR, `${clashFileBasename}.json`)
] ]
); );
} }
@ -176,9 +172,9 @@ async function transformRuleset(parentSpan: Span, sourcePath: string, relativePa
if (res === $rm) { if (res === $rm) {
return removeFiles([ return removeFiles([
path.resolve(outputSurgeDir, relativePath), path.resolve(OUTPUT_SURGE_DIR, relativePath),
path.resolve(outputClashDir, `${clashFileBasename}.txt`), path.resolve(OUTPUT_CLASH_DIR, `${clashFileBasename}.txt`),
path.resolve(outputSingboxDir, `${clashFileBasename}.json`) path.resolve(OUTPUT_SINGBOX_DIR, `${clashFileBasename}.json`)
]); ]);
} }
@ -201,9 +197,9 @@ async function transformRuleset(parentSpan: Span, sourcePath: string, relativePa
lines, lines,
'ruleset', 'ruleset',
[ [
path.resolve(outputSurgeDir, relativePath), path.resolve(OUTPUT_SURGE_DIR, relativePath),
path.resolve(outputClashDir, `${clashFileBasename}.txt`), path.resolve(OUTPUT_CLASH_DIR, `${clashFileBasename}.txt`),
path.resolve(outputSingboxDir, `${clashFileBasename}.json`) path.resolve(OUTPUT_SINGBOX_DIR, `${clashFileBasename}.json`)
] ]
); );
}); });

View File

@ -1,3 +1,4 @@
import { OUTPUT_CLASH_DIR, OUTPUT_SURGE_DIR } from './constants/dir';
import { compareAndWriteFile } from './lib/create-file'; import { compareAndWriteFile } from './lib/create-file';
import { task } from './trace'; import { task } from './trace';
import path from 'node:path'; import path from 'node:path';
@ -8,15 +9,12 @@ const DEPRECATED_FILES = [
['domainset/reject_phishing', 'This file has been merged with domainset/reject'] ['domainset/reject_phishing', 'This file has been merged with domainset/reject']
]; ];
const outputSurgeDir = path.resolve(__dirname, '../List');
const outputClashDir = path.resolve(__dirname, '../Clash');
export const buildDeprecateFiles = task(require.main === module, __filename)((span) => span.traceChildAsync('create deprecated files', async (childSpan) => { export const buildDeprecateFiles = task(require.main === module, __filename)((span) => span.traceChildAsync('create deprecated files', async (childSpan) => {
const promises: Array<Promise<unknown>> = []; const promises: Array<Promise<unknown>> = [];
for (const [filePath, description] of DEPRECATED_FILES) { for (const [filePath, description] of DEPRECATED_FILES) {
const surgeFile = path.resolve(outputSurgeDir, `${filePath}.conf`); const surgeFile = path.resolve(OUTPUT_SURGE_DIR, `${filePath}.conf`);
const clashFile = path.resolve(outputClashDir, `${filePath}.txt`); const clashFile = path.resolve(OUTPUT_CLASH_DIR, `${filePath}.txt`);
const content = [ const content = [
'#########################################', '#########################################',

View File

@ -10,10 +10,11 @@ import { createMemoizedPromise } from './lib/memo-promise';
import * as yaml from 'yaml'; import * as yaml from 'yaml';
import { appendArrayInPlace } from './lib/append-array-in-place'; import { appendArrayInPlace } from './lib/append-array-in-place';
import { output, writeFile } from './lib/misc'; import { output, writeFile } from './lib/misc';
import { OUTPUT_INTERNAL_DIR, OUTPUT_MODULES_DIR, SOURCE_DIR } from './constants/dir';
export const getDomesticAndDirectDomainsRulesetPromise = createMemoizedPromise(async () => { export const getDomesticAndDirectDomainsRulesetPromise = createMemoizedPromise(async () => {
const domestics = await readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/domestic.conf')); const domestics = await readFileIntoProcessedArray(path.join(SOURCE_DIR, 'non_ip/domestic.conf'));
const directs = await readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/direct.conf')); const directs = await readFileIntoProcessedArray(path.resolve(SOURCE_DIR, 'non_ip/direct.conf'));
const lans: string[] = []; const lans: string[] = [];
Object.entries(DOMESTICS).forEach(([, { domains }]) => { Object.entries(DOMESTICS).forEach(([, { domains }]) => {
@ -91,10 +92,10 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
]) ])
]) ])
], ],
path.resolve(__dirname, '../Modules/sukka_local_dns_mapping.sgmodule') path.resolve(OUTPUT_MODULES_DIR, 'sukka_local_dns_mapping.sgmodule')
), ),
writeFile( writeFile(
path.resolve(__dirname, '../Internal/clash_nameserver_policy.yaml'), path.join(OUTPUT_INTERNAL_DIR, 'clash_nameserver_policy.yaml'),
yaml.stringify( yaml.stringify(
{ {
dns: { dns: {

View File

@ -6,6 +6,7 @@ import { getChnCidrPromise } from './build-chn-cidr';
import { NON_CN_CIDR_INCLUDED_IN_CHNROUTE, RESERVED_IPV4_CIDR } from './constants/cidr'; import { NON_CN_CIDR_INCLUDED_IN_CHNROUTE, RESERVED_IPV4_CIDR } from './constants/cidr';
import { writeFile } from './lib/misc'; import { writeFile } from './lib/misc';
import { OUTPUT_INTERNAL_DIR } from './constants/dir';
export const buildInternalReverseChnCIDR = task(require.main === module, __filename)(async () => { export const buildInternalReverseChnCIDR = task(require.main === module, __filename)(async () => {
const [cidr] = await getChnCidrPromise(); const [cidr] = await getChnCidrPromise();
@ -21,8 +22,7 @@ export const buildInternalReverseChnCIDR = task(require.main === module, __filen
) )
); );
const outputDir = path.resolve(__dirname, '../Internal'); const outputFile = path.join(OUTPUT_INTERNAL_DIR, 'reversed-chn-cidr.txt');
const outputFile = path.join(outputDir, 'reversed-chn-cidr.txt');
return writeFile( return writeFile(
outputFile, outputFile,

View File

@ -1,69 +1,52 @@
import path from 'node:path'; import path from 'node:path';
import fs from 'node:fs'; import fs from 'node:fs';
import fsp from 'node:fs/promises'; import fsp from 'node:fs/promises';
import { task } from './trace'; import { task } from './trace';
import { treeDir } from './lib/tree-dir'; import { treeDir } from './lib/tree-dir';
import type { TreeType, TreeTypeArray } from './lib/tree-dir'; import type { TreeType, TreeTypeArray } from './lib/tree-dir';
import { fdir as Fdir } from 'fdir';
import Trie from 'mnemonist/trie'; import { OUTPUT_MOCK_DIR, OUTPUT_MODULES_DIR, PUBLIC_DIR, ROOT_DIR } from './constants/dir';
import { writeFile } from './lib/misc'; import { writeFile } from './lib/misc';
import picocolors from 'picocolors';
const rootPath = path.resolve(__dirname, '../'); const mockDir = path.join(ROOT_DIR, 'Mock');
const publicPath = path.resolve(__dirname, '../public'); const modulesDir = path.join(ROOT_DIR, 'Modules');
const folderAndFilesToBeDeployed = [ const copyDirContents = async (srcDir: string, destDir: string) => {
`Mock${path.sep}`, const promises: Array<Promise<void>> = [];
`List${path.sep}`,
`Clash${path.sep}`, for await (const entry of await fsp.opendir(srcDir)) {
`sing-box${path.sep}`, const src = path.join(srcDir, entry.name);
`Modules${path.sep}`, const dest = path.join(destDir, entry.name);
`Script${path.sep}`, if (entry.isDirectory()) {
`Internal${path.sep}`, console.warn(picocolors.red('[build public] cant copy directory'), src);
'LICENSE' } else {
]; promises.push(fsp.copyFile(src, dest, fs.constants.COPYFILE_FICLONE));
}
}
return Promise.all(promises);
};
export const buildPublic = task(require.main === module, __filename)(async (span) => { export const buildPublic = task(require.main === module, __filename)(async (span) => {
fs.mkdirSync(publicPath, { recursive: true }); await span.traceChildAsync('copy rest of the files', async () => {
await Promise.all([
fsp.mkdir(OUTPUT_MODULES_DIR, { recursive: true }),
fsp.mkdir(OUTPUT_MOCK_DIR, { recursive: true })
]);
await span await Promise.all([
.traceChild('copy public files') copyDirContents(modulesDir, OUTPUT_MODULES_DIR),
.traceAsyncFn(async () => { copyDirContents(mockDir, OUTPUT_MOCK_DIR)
const trie = Trie.from(await new Fdir() ]);
.withRelativePaths() });
.exclude((dirName) => (
dirName === 'node_modules'
|| dirName === 'Build'
|| dirName === 'public'
|| dirName[0] === '.'
))
.crawl(rootPath)
.withPromise());
const filesToBeCopied = folderAndFilesToBeDeployed.flatMap(folderOrFile => trie.find(folderOrFile));
return Promise.all(filesToBeCopied.map(file => {
const src = path.join(rootPath, file);
const dest = path.join(publicPath, file);
const destParen = path.dirname(dest);
if (!fs.existsSync(destParen)) {
fs.mkdirSync(destParen, { recursive: true });
}
return fsp.copyFile(
src,
dest,
fs.constants.COPYFILE_FICLONE
);
}));
});
const html = await span const html = await span
.traceChild('generate index.html') .traceChild('generate index.html')
.traceAsyncFn(() => treeDir(publicPath).then(generateHtml)); .traceAsyncFn(() => treeDir(PUBLIC_DIR).then(generateHtml));
return writeFile(path.join(publicPath, 'index.html'), html); return writeFile(path.join(PUBLIC_DIR, 'index.html'), html);
}); });
const priorityOrder: Record<'default' | string & {}, number> = { const priorityOrder: Record<'default' | string & {}, number> = {

View File

@ -21,8 +21,9 @@ import { getPhishingDomains } from './lib/get-phishing-domains';
import { setAddFromArray, setAddFromArrayCurried } from './lib/set-add-from-array'; import { setAddFromArray, setAddFromArrayCurried } from './lib/set-add-from-array';
import { output } from './lib/misc'; import { output } from './lib/misc';
import { appendArrayInPlace } from './lib/append-array-in-place'; import { appendArrayInPlace } from './lib/append-array-in-place';
import { OUTPUT_INTERNAL_DIR, SOURCE_DIR } from './constants/dir';
const getRejectSukkaConfPromise = readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/reject_sukka.conf')); const getRejectSukkaConfPromise = readFileIntoProcessedArray(path.join(SOURCE_DIR, 'domainset/reject_sukka.conf'));
export const buildRejectDomainSet = task(require.main === module, __filename)(async (span) => { export const buildRejectDomainSet = task(require.main === module, __filename)(async (span) => {
/** Whitelists */ /** Whitelists */
@ -214,7 +215,7 @@ export const buildRejectDomainSet = task(require.main === module, __filename)(as
compareAndWriteFile( compareAndWriteFile(
span, span,
rejectDomainsStats, rejectDomainsStats,
path.resolve(__dirname, '../Internal/reject-stats.txt') path.join(OUTPUT_INTERNAL_DIR, 'reject-stats.txt')
) )
]); ]);
}); });

View File

@ -4,6 +4,7 @@ import { compareAndWriteFile } from './lib/create-file';
import { DIRECTS, LANS } from '../Source/non_ip/direct'; import { DIRECTS, LANS } from '../Source/non_ip/direct';
import * as yaml from 'yaml'; import * as yaml from 'yaml';
import { writeFile } from './lib/misc'; import { writeFile } from './lib/misc';
import { OUTPUT_INTERNAL_DIR, OUTPUT_MODULES_DIR } from './constants/dir';
const HOSTNAMES = [ const HOSTNAMES = [
// Network Detection, Captive Portal // Network Detection, Captive Portal
@ -59,10 +60,10 @@ export const buildAlwaysRealIPModule = task(require.main === module, __filename)
'[General]', '[General]',
`always-real-ip = %APPEND% ${HOSTNAMES.concat(surge).join(', ')}` `always-real-ip = %APPEND% ${HOSTNAMES.concat(surge).join(', ')}`
], ],
path.resolve(__dirname, '../Modules/sukka_common_always_realip.sgmodule') path.resolve(OUTPUT_MODULES_DIR, 'sukka_common_always_realip.sgmodule')
), ),
writeFile( writeFile(
path.resolve(__dirname, '../Internal/clash_fake_ip_filter.yaml'), path.join(OUTPUT_INTERNAL_DIR, 'clash_fake_ip_filter.yaml'),
yaml.stringify( yaml.stringify(
{ {
dns: { dns: {

View File

@ -3,6 +3,7 @@ import { task } from './trace';
import { compareAndWriteFile } from './lib/create-file'; import { compareAndWriteFile } from './lib/create-file';
import { getHostname } from 'tldts'; import { getHostname } from 'tldts';
import { isTruthy } from './lib/misc'; import { isTruthy } from './lib/misc';
import { OUTPUT_MODULES_DIR } from './constants/dir';
function escapeRegExp(string = '') { function escapeRegExp(string = '') {
const reRegExpChar = /[$()*+.?[\\\]^{|}]/g; const reRegExpChar = /[$()*+.?[\\\]^{|}]/g;
@ -148,6 +149,6 @@ export const buildRedirectModule = task(require.main === module, __filename)(asy
...REDIRECT_MIRROR.map(([from, to]) => `^https?://${escapeRegExp(from)}(.*) ${to}$1 header`), ...REDIRECT_MIRROR.map(([from, to]) => `^https?://${escapeRegExp(from)}(.*) ${to}$1 header`),
...REDIRECT_FAKEWEBSITES.map(([from, to]) => `^https?://(www.)?${escapeRegExp(from)} ${to} 307`) ...REDIRECT_FAKEWEBSITES.map(([from, to]) => `^https?://(www.)?${escapeRegExp(from)} ${to} 307`)
], ],
path.resolve(__dirname, '../Modules/sukka_url_redirect.sgmodule') path.join(OUTPUT_MODULES_DIR, 'sukka_url_redirect.sgmodule')
); );
}); });

View File

@ -12,6 +12,7 @@ import { compareAndWriteFile } from './lib/create-file';
import { getMicrosoftCdnRulesetPromise } from './build-microsoft-cdn'; import { getMicrosoftCdnRulesetPromise } from './build-microsoft-cdn';
import { isTruthy } from './lib/misc'; import { isTruthy } from './lib/misc';
import { appendArrayInPlace } from './lib/append-array-in-place'; import { appendArrayInPlace } from './lib/append-array-in-place';
import { OUTPUT_INTERNAL_DIR, SOURCE_DIR } from './constants/dir';
const POLICY_GROUPS: Array<[name: string, insertProxy: boolean, insertDirect: boolean]> = [ const POLICY_GROUPS: Array<[name: string, insertProxy: boolean, insertDirect: boolean]> = [
['Default Proxy', true, false], ['Default Proxy', true, false],
@ -55,18 +56,18 @@ export const buildSSPanelUIMAppProfile = task(require.main === module, __filenam
), ),
getAppleCdnDomainsPromise().then(domains => domains.map(domain => `DOMAIN-SUFFIX,${domain}`)), getAppleCdnDomainsPromise().then(domains => domains.map(domain => `DOMAIN-SUFFIX,${domain}`)),
getMicrosoftCdnRulesetPromise().then(surgeRulesetToClashClassicalTextRuleset), getMicrosoftCdnRulesetPromise().then(surgeRulesetToClashClassicalTextRuleset),
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/apple_cn.conf')), readFileIntoProcessedArray(path.join(SOURCE_DIR, 'non_ip/apple_cn.conf')),
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/neteasemusic.conf')).then(surgeRulesetToClashClassicalTextRuleset), readFileIntoProcessedArray(path.join(SOURCE_DIR, 'non_ip/neteasemusic.conf')).then(surgeRulesetToClashClassicalTextRuleset),
// microsoft & apple - domains // microsoft & apple - domains
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/microsoft.conf')), readFileIntoProcessedArray(path.join(SOURCE_DIR, 'non_ip/microsoft.conf')),
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/apple_services.conf')).then(surgeRulesetToClashClassicalTextRuleset), readFileIntoProcessedArray(path.join(SOURCE_DIR, 'non_ip/apple_services.conf')).then(surgeRulesetToClashClassicalTextRuleset),
// stream - domains // stream - domains
surgeRulesetToClashClassicalTextRuleset(AllStreamServices.flatMap((i) => i.rules)), surgeRulesetToClashClassicalTextRuleset(AllStreamServices.flatMap((i) => i.rules)),
// steam - domains // steam - domains
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/steam.conf')).then(surgeDomainsetToClashRuleset), readFileIntoProcessedArray(path.join(SOURCE_DIR, 'domainset/steam.conf')).then(surgeDomainsetToClashRuleset),
// global - domains // global - domains
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/global.conf')).then(surgeRulesetToClashClassicalTextRuleset), readFileIntoProcessedArray(path.join(SOURCE_DIR, 'non_ip/global.conf')).then(surgeRulesetToClashClassicalTextRuleset),
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/telegram.conf')).then(surgeRulesetToClashClassicalTextRuleset), readFileIntoProcessedArray(path.join(SOURCE_DIR, 'non_ip/telegram.conf')).then(surgeRulesetToClashClassicalTextRuleset),
// domestic - ip cidr // domestic - ip cidr
getChnCidrPromise().then(([cidrs4, cidrs6]) => [ getChnCidrPromise().then(([cidrs4, cidrs6]) => [
...cidrs4.map(cidr => `IP-CIDR,${cidr}`), ...cidrs4.map(cidr => `IP-CIDR,${cidr}`),
@ -83,7 +84,7 @@ export const buildSSPanelUIMAppProfile = task(require.main === module, __filenam
// global - ip cidr // global - ip cidr
getTelegramCIDRPromise(), getTelegramCIDRPromise(),
// lan - ip cidr // lan - ip cidr
readFileIntoProcessedArray(path.resolve(__dirname, '../Source/ip/lan.conf')) readFileIntoProcessedArray(path.join(SOURCE_DIR, 'ip/lan.conf'))
] as const); ] as const);
const telegramCidrs = rawTelegramCidrs.map(removeNoResolved); const telegramCidrs = rawTelegramCidrs.map(removeNoResolved);
@ -121,7 +122,7 @@ export const buildSSPanelUIMAppProfile = task(require.main === module, __filenam
await compareAndWriteFile( await compareAndWriteFile(
span, span,
output, output,
path.resolve(__dirname, '../Internal/appprofile.php') path.resolve(OUTPUT_INTERNAL_DIR, 'appprofile.php')
); );
}); });

13
Build/constants/dir.ts Normal file
View File

@ -0,0 +1,13 @@
import path from 'node:path';
export const ROOT_DIR = path.resolve(__dirname, '../..');
export const SOURCE_DIR = path.join(ROOT_DIR, 'Source');
export const PUBLIC_DIR = path.resolve(ROOT_DIR, 'public');
export const OUTPUT_SURGE_DIR = path.join(PUBLIC_DIR, 'List');
export const OUTPUT_CLASH_DIR = path.resolve(PUBLIC_DIR, 'Clash');
export const OUTPUT_SINGBOX_DIR = path.resolve(PUBLIC_DIR, 'sing-box');
export const OUTPUT_MODULES_DIR = path.resolve(PUBLIC_DIR, 'Modules');
export const OUTPUT_INTERNAL_DIR = path.resolve(PUBLIC_DIR, 'Internal');
export const OUTPUT_MOCK_DIR = path.resolve(PUBLIC_DIR, 'Mock');

View File

@ -1,9 +1,11 @@
import { task } from './trace'; import { task } from './trace';
import path from 'node:path'; import path from 'node:path';
import fs from 'node:fs'; import fs from 'node:fs';
import fsp from 'node:fs/promises';
import { Readable } from 'node:stream'; import { Readable } from 'node:stream';
import { pipeline } from 'node:stream/promises'; import { pipeline } from 'node:stream/promises';
import { fetchWithRetry } from './lib/fetch-retry'; import { fetchWithRetry } from './lib/fetch-retry';
import { OUTPUT_MOCK_DIR } from './constants/dir';
const ASSETS_LIST = { const ASSETS_LIST = {
'www-google-analytics-com_ga.js': 'https://raw.githubusercontent.com/AdguardTeam/Scriptlets/master/dist/redirect-files/google-analytics-ga.js', 'www-google-analytics-com_ga.js': 'https://raw.githubusercontent.com/AdguardTeam/Scriptlets/master/dist/redirect-files/google-analytics-ga.js',
@ -13,19 +15,20 @@ const ASSETS_LIST = {
'amazon-adsystem-com_amazon-apstag.js': 'https://raw.githubusercontent.com/AdguardTeam/Scriptlets/master/dist/redirect-files/amazon-apstag.js' 'amazon-adsystem-com_amazon-apstag.js': 'https://raw.githubusercontent.com/AdguardTeam/Scriptlets/master/dist/redirect-files/amazon-apstag.js'
} as const; } as const;
const mockDir = path.resolve(__dirname, '../Mock');
export const downloadMockAssets = task(require.main === module, __filename)((span) => Promise.all(Object.entries(ASSETS_LIST).map( export const downloadMockAssets = task(require.main === module, __filename)((span) => Promise.all(Object.entries(ASSETS_LIST).map(
([filename, url]) => span ([filename, url]) => span
.traceChildAsync(url, () => fetchWithRetry(url).then(res => { .traceChildAsync(url, async () => {
const src = path.join(mockDir, filename); const res = await fetchWithRetry(url);
const src = path.join(OUTPUT_MOCK_DIR, filename);
if (!res.body) { if (!res.body) {
throw new Error(`Empty body from ${url}`); throw new Error(`Empty body from ${url}`);
} }
await fsp.mkdir(OUTPUT_MOCK_DIR, { recursive: true });
return pipeline( return pipeline(
Readable.fromWeb(res.body), Readable.fromWeb(res.body),
fs.createWriteStream(src, 'utf-8') fs.createWriteStream(src, 'utf-8')
); );
})) })
))); )));

View File

@ -1,57 +1,17 @@
import { existsSync, createWriteStream } from 'node:fs'; import { createWriteStream } from 'node:fs';
import { mkdir } from 'node:fs/promises'; import { mkdir } from 'node:fs/promises';
import path from 'node:path'; import path from 'node:path';
import { pipeline } from 'node:stream/promises'; import { pipeline } from 'node:stream/promises';
import { readFileByLine } from './lib/fetch-text-by-line';
import { isCI } from 'ci-info';
import { task } from './trace'; import { task } from './trace';
import { defaultRequestInit, fetchWithRetry } from './lib/fetch-retry'; import { defaultRequestInit, fetchWithRetry } from './lib/fetch-retry';
import tarStream from 'tar-stream'; import tarStream from 'tar-stream';
import zlib from 'node:zlib'; import zlib from 'node:zlib';
import { Readable } from 'node:stream'; import { Readable } from 'node:stream';
const IS_READING_BUILD_OUTPUT = 1 << 2;
const ALL_FILES_EXISTS = 1 << 3;
const GITHUB_CODELOAD_URL = 'https://codeload.github.com/sukkalab/ruleset.skk.moe/tar.gz/master'; const GITHUB_CODELOAD_URL = 'https://codeload.github.com/sukkalab/ruleset.skk.moe/tar.gz/master';
const GITLAB_CODELOAD_URL = 'https://gitlab.com/SukkaW/ruleset.skk.moe/-/archive/master/ruleset.skk.moe-master.tar.gz'; const GITLAB_CODELOAD_URL = 'https://gitlab.com/SukkaW/ruleset.skk.moe/-/archive/master/ruleset.skk.moe-master.tar.gz';
export const downloadPreviousBuild = task(require.main === module, __filename)(async (span) => { export const downloadPreviousBuild = task(require.main === module, __filename)(async (span) => {
const buildOutputList: string[] = [];
let flag = 1 | ALL_FILES_EXISTS;
await span
.traceChild('read .gitignore')
.traceAsyncFn(async () => {
for await (const line of readFileByLine(path.resolve(__dirname, '../.gitignore'))) {
if (line === '# $ build output') {
flag = flag | IS_READING_BUILD_OUTPUT;
continue;
}
if (!(flag & IS_READING_BUILD_OUTPUT)) {
continue;
}
buildOutputList.push(line);
if (!isCI && !existsSync(path.join(__dirname, '..', line))) {
flag = flag & ~ALL_FILES_EXISTS;
}
}
});
if (isCI) {
flag = flag & ~ALL_FILES_EXISTS;
}
if (flag & ALL_FILES_EXISTS) {
console.log('All files exists, skip download.');
return;
}
const filesList = buildOutputList.map(f => path.join('ruleset.skk.moe-master', f));
const tarGzUrl = await span.traceChildAsync('get tar.gz url', async () => { const tarGzUrl = await span.traceChildAsync('get tar.gz url', async () => {
const resp = await fetchWithRetry(GITHUB_CODELOAD_URL, { const resp = await fetchWithRetry(GITHUB_CODELOAD_URL, {
...defaultRequestInit, ...defaultRequestInit,
@ -68,6 +28,8 @@ export const downloadPreviousBuild = task(require.main === module, __filename)(a
return GITHUB_CODELOAD_URL; return GITHUB_CODELOAD_URL;
}); });
const publicDir = path.resolve(__dirname, '..', 'public');
return span.traceChildAsync('download & extract previoud build', async () => { return span.traceChildAsync('download & extract previoud build', async () => {
const resp = await fetchWithRetry(tarGzUrl, { const resp = await fetchWithRetry(tarGzUrl, {
headers: { headers: {
@ -112,14 +74,9 @@ export const downloadPreviousBuild = task(require.main === module, __filename)(a
entry.resume(); // Drain the entry entry.resume(); // Drain the entry
continue; continue;
} }
// filter entry
if (!filesList.some(f => entry.header.name.startsWith(f))) {
entry.resume(); // Drain the entry
continue;
}
const relativeEntryPath = entry.header.name.replace(pathPrefix, ''); const relativeEntryPath = entry.header.name.replace(pathPrefix, '');
const targetPath = path.join(__dirname, '..', relativeEntryPath); const targetPath = path.join(publicDir, relativeEntryPath);
await mkdir(path.dirname(targetPath), { recursive: true }); await mkdir(path.dirname(targetPath), { recursive: true });
await pipeline(entry, createWriteStream(targetPath)); await pipeline(entry, createWriteStream(targetPath));

View File

@ -3,8 +3,9 @@ import { processLine, processLineFromReadline } from './process-line';
import { readFileByLine } from './fetch-text-by-line'; import { readFileByLine } from './fetch-text-by-line';
import path from 'node:path'; import path from 'node:path';
import fsp from 'node:fs/promises'; import fsp from 'node:fs/promises';
import { SOURCE_DIR } from '../constants/dir';
const file = path.resolve(__dirname, '../../Source/domainset/cdn.conf'); const file = path.join(SOURCE_DIR, 'domainset/cdn.conf');
group('read file by line', () => { group('read file by line', () => {
bench('readFileByLine', () => processLineFromReadline(readFileByLine(file))); bench('readFileByLine', () => processLineFromReadline(readFileByLine(file)));

View File

@ -1,6 +1,7 @@
import path, { dirname } from 'node:path'; import path, { dirname } from 'node:path';
import fs from 'node:fs'; import fs from 'node:fs';
import fsp from 'node:fs/promises'; import fsp from 'node:fs/promises';
import { OUTPUT_CLASH_DIR, OUTPUT_SINGBOX_DIR, OUTPUT_SURGE_DIR } from '../constants/dir';
export const isTruthy = <T>(i: T | 0 | '' | false | null | undefined): i is T => !!i; export const isTruthy = <T>(i: T | 0 | '' | false | null | undefined): i is T => !!i;
@ -52,10 +53,6 @@ export const domainWildCardToRegex = (domain: string) => {
return result; return result;
}; };
const OUTPUT_SURGE_DIR = path.resolve(__dirname, '../../List');
const OUTPUT_CLASH_DIR = path.resolve(__dirname, '../../Clash');
const OUTPUT_SINGBOX_DIR = path.resolve(__dirname, '../../sing-box');
export const output = (id: string, type: 'non_ip' | 'ip' | 'domainset') => { export const output = (id: string, type: 'non_ip' | 'ip' | 'domainset') => {
return [ return [
path.join(OUTPUT_SURGE_DIR, type, id + '.conf'), path.join(OUTPUT_SURGE_DIR, type, id + '.conf'),

View File

@ -25,6 +25,11 @@ export const treeDir = async (rootPath: string): Promise<TreeTypeArray> => {
const walk = async (dir: string, node: TreeTypeArray, dirRelativeToRoot = ''): Promise<VoidOrVoidArray> => { const walk = async (dir: string, node: TreeTypeArray, dirRelativeToRoot = ''): Promise<VoidOrVoidArray> => {
const promises: Array<Promise<VoidOrVoidArray>> = []; const promises: Array<Promise<VoidOrVoidArray>> = [];
for await (const child of await fsp.opendir(dir)) { for await (const child of await fsp.opendir(dir)) {
// Ignore hidden files
if (child.name[0] === '.' || child.name === 'CNAME') {
continue;
}
const childFullPath = child.parentPath + sep + child.name; const childFullPath = child.parentPath + sep + child.name;
const childRelativeToRoot = dirRelativeToRoot + sep + child.name; const childRelativeToRoot = dirRelativeToRoot + sep + child.name;

View File

@ -2,8 +2,7 @@ import path from 'node:path';
import fsp from 'node:fs/promises'; import fsp from 'node:fs/promises';
import { fdir as Fdir } from 'fdir'; import { fdir as Fdir } from 'fdir';
import { readFileByLine } from './lib/fetch-text-by-line'; import { readFileByLine } from './lib/fetch-text-by-line';
import { SOURCE_DIR } from './constants/dir';
const sourceDir = path.resolve(__dirname, '../Source');
(async () => { (async () => {
const promises: Array<Promise<unknown>> = []; const promises: Array<Promise<unknown>> = [];
@ -27,7 +26,7 @@ const sourceDir = path.resolve(__dirname, '../Source');
return true; return true;
}) })
.crawl(sourceDir) .crawl(SOURCE_DIR)
.withPromise(); .withPromise();
for (let i = 0, len = paths.length; i < len; i++) { for (let i = 0, len = paths.length; i < len; i++) {

View File

@ -5,6 +5,7 @@ import { createTrie } from './lib/trie';
import { parse } from 'csv-parse/sync'; import { parse } from 'csv-parse/sync';
import { readFileByLine } from './lib/fetch-text-by-line'; import { readFileByLine } from './lib/fetch-text-by-line';
import path from 'node:path'; import path from 'node:path';
import { SOURCE_DIR } from './constants/dir';
export const parseGfwList = async () => { export const parseGfwList = async () => {
const whiteSet = new Set<string>(); const whiteSet = new Set<string>();
@ -105,8 +106,8 @@ export const parseGfwList = async () => {
}; };
await Promise.all([ await Promise.all([
runAgainstRuleset(path.resolve(__dirname, '../Source/non_ip/global.conf')), runAgainstRuleset(path.join(SOURCE_DIR, 'non_ip/global.conf')),
runAgainstRuleset(path.resolve(__dirname, '../Source/non_ip/telegram.conf')), runAgainstRuleset(path.join(SOURCE_DIR, 'non_ip/telegram.conf')),
runAgainstRuleset(path.resolve(__dirname, '../List/non_ip/stream.conf')) runAgainstRuleset(path.resolve(__dirname, '../List/non_ip/stream.conf'))
]); ]);