import path from 'node:path'; import fs from 'node:fs'; import fsp from 'node:fs/promises'; import { task } from './trace'; import { treeDir } from './lib/tree-dir'; import type { TreeType, TreeTypeArray } from './lib/tree-dir'; import { OUTPUT_MOCK_DIR, OUTPUT_MODULES_DIR, PUBLIC_DIR, ROOT_DIR } from './constants/dir'; import { mkdirp, writeFile } from './lib/misc'; import picocolors from 'picocolors'; const mockDir = path.join(ROOT_DIR, 'Mock'); const modulesDir = path.join(ROOT_DIR, 'Modules'); const copyDirContents = async (srcDir: string, destDir: string) => { const promises: Array> = []; for await (const entry of await fsp.opendir(srcDir)) { const src = path.join(srcDir, entry.name); const dest = path.join(destDir, entry.name); if (entry.isDirectory()) { console.warn(picocolors.red('[build public] cant copy directory'), src); } 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) => { await span.traceChildAsync('copy rest of the files', async () => { await Promise.all([ mkdirp(OUTPUT_MODULES_DIR), mkdirp(OUTPUT_MOCK_DIR) ]); await Promise.all([ copyDirContents(modulesDir, OUTPUT_MODULES_DIR), copyDirContents(mockDir, OUTPUT_MOCK_DIR) ]); }); const html = await span .traceChild('generate index.html') .traceAsyncFn(() => treeDir(PUBLIC_DIR).then(generateHtml)); return writeFile(path.join(PUBLIC_DIR, 'index.html'), html); }); const priorityOrder: Record<'default' | string & {}, number> = { domainset: 1, non_ip: 2, ip: 3, List: 10, Surge: 11, Clash: 12, 'sing-box': 13, Modules: 20, Script: 30, Mock: 40, Assets: 50, Internal: 60, LICENSE: 70, default: Number.MAX_VALUE }; const prioritySorter = (a: TreeType, b: TreeType) => { return ((priorityOrder[a.name] || priorityOrder.default) - (priorityOrder[b.name] || priorityOrder.default)) || a.name.localeCompare(b.name); }; const html = (string: TemplateStringsArray, ...values: any[]) => string.reduce((acc, str, i) => acc + str + (values[i] ?? ''), ''); const walk = (tree: TreeTypeArray) => { let result = ''; tree.sort(prioritySorter); for (let i = 0, len = tree.length; i < len; i++) { const entry = tree[i]; if (entry.type === 'directory') { result += html`
  • ${entry.name}
  • `; } else if (/* entry.type === 'file' && */ entry.name !== 'index.html') { result += html`
  • ${entry.name}
  • `; } } return result; }; function generateHtml(tree: TreeTypeArray) { return html` Surge Ruleset Server | Sukka (@SukkaW)

    Sukka Ruleset Server

    Made by Sukka | Source @ GitHub | Licensed under AGPL-3.0

    Last Build: ${new Date().toISOString()}


    `; }