import path from 'node:path'; import fs from 'node:fs'; import fsp from 'node:fs/promises'; import { task } from './trace'; import { treeDir, TreeFileType } from './lib/tree-dir'; import type { TreeType, TreeTypeArray } from './lib/tree-dir'; import { OUTPUT_MOCK_DIR, OUTPUT_MODULES_RULES_DIR, PUBLIC_DIR, ROOT_DIR } from './constants/dir'; import { fastStringCompare, writeFile } from './lib/misc'; import type { VoidOrVoidArray } from './lib/misc'; import picocolors from 'picocolors'; import { tagged as html } from 'foxts/tagged'; import { compareAndWriteFile } from './lib/create-file'; const priorityOrder: Record<'default' | string & {}, number> = { LICENSE: 0, domainset: 10, non_ip: 20, ip: 30, List: 40, Surge: 50, Clash: 60, 'sing-box': 70, Surfboard: 80, LegacyClashPremium: 81, Modules: 90, Script: 100, Mock: 110, Assets: 120, Internal: 130, default: Number.MAX_VALUE }; const closedRootFolders = [ 'Mock', 'Internal' ]; async function copyDirContents(srcDir: string, destDir: string, promises: Array> = []): Promise>> { 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 promises; } export const buildPublic = task(require.main === module, __filename)(async (span) => { await span.traceChildAsync('copy rest of the files', async () => { const p: Array> = []; fs.mkdirSync(OUTPUT_MODULES_RULES_DIR, { recursive: true }); p.push(copyDirContents(path.join(ROOT_DIR, 'Modules'), OUTPUT_MODULES_RULES_DIR, p)); fs.mkdirSync(OUTPUT_MOCK_DIR, { recursive: true }); p.push(copyDirContents(path.join(ROOT_DIR, 'Mock'), OUTPUT_MOCK_DIR, p)); await Promise.all(p); }); const html = await span .traceChild('generate index.html') .traceAsyncFn(() => treeDir(PUBLIC_DIR).then(generateHtml)); await Promise.all([ compareAndWriteFile( span, [ '/*', ' cache-control: public, max-age=240, stale-while-revalidate=60, stale-if-error=15', 'https://:project.pages.dev/*', ' X-Robots-Tag: noindex', ...Object.keys(priorityOrder) .map((name) => `/${name}/*\n content-type: text/plain; charset=utf-8\n X-Robots-Tag: noindex`) ], path.join(PUBLIC_DIR, '_headers') ), compareAndWriteFile( span, [ '#
',
        '#########################################',
        '# Sukka\'s Ruleset - 404 Not Found',
        '################## EOF ##################
' ], path.join(PUBLIC_DIR, '404.html') ), compareAndWriteFile( span, [ '# This is a Robot-managed repo containing only output', '# The source code is located at [Sukkaw/Surge](https://github.com/Sukkaw/Surge)', '# Please follow the development at the source code repo instead', '', '![GitHub repo size](https://img.shields.io/github/repo-size/sukkalab/ruleset.skk.moe?style=flat-square)' ], path.join(PUBLIC_DIR, 'README.md') ) ]); return writeFile(path.join(PUBLIC_DIR, 'index.html'), html); }); const prioritySorter = (a: TreeType, b: TreeType) => ((priorityOrder[a.name] || priorityOrder.default) - (priorityOrder[b.name] || priorityOrder.default)) || fastStringCompare(a.name, b.name); function treeHtml(tree: TreeTypeArray, level = 0, closedFolderList: string[] = []): string { let result = ''; tree.sort(prioritySorter); for (let i = 0, len = tree.length; i < len; i++) { const entry = tree[i]; const open = closedFolderList.includes(entry.name) ? '' : (level === 0 ? 'open' : ''); if (entry.type === TreeFileType.DIRECTORY) { result += html`
  • ${entry.name}
      ${treeHtml(entry.children, level + 1)}
  • `; } else if (/* entry.type === 'file' && */ !entry.name.endsWith('.html') && !entry.name.startsWith('_')) { 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()}


      ${treeHtml(tree, 0, closedRootFolders)}
    `; }