create-file.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // @ts-check
  2. import { readFileByLine } from './fetch-text-by-line';
  3. import { surgeDomainsetToClashDomainset, surgeRulesetToClashClassicalTextRuleset } from './clash';
  4. import { traceAsync } from './trace-runner';
  5. export async function compareAndWriteFile(linesA: string[], filePath: string) {
  6. let isEqual = true;
  7. const file = Bun.file(filePath);
  8. const linesALen = linesA.length;
  9. if (!(await file.exists())) {
  10. console.log(`${filePath} does not exists, writing...`);
  11. isEqual = false;
  12. } else if (linesALen === 0) {
  13. console.log(`Nothing to write to ${filePath}...`);
  14. isEqual = false;
  15. } else {
  16. let index = 0;
  17. for await (const lineB of readFileByLine(file)) {
  18. const lineA = linesA[index];
  19. index++;
  20. if (lineA == null) {
  21. // The file becomes smaller
  22. isEqual = false;
  23. break;
  24. }
  25. if (lineA[0] === '#' && lineB[0] === '#') {
  26. continue;
  27. }
  28. if (lineA !== lineB) {
  29. isEqual = false;
  30. break;
  31. }
  32. }
  33. if (isEqual && index !== linesALen) {
  34. // The file becomes larger
  35. isEqual = false;
  36. }
  37. }
  38. if (isEqual) {
  39. console.log(`Same Content, bail out writing: ${filePath}`);
  40. return;
  41. }
  42. await traceAsync(`Writing ${filePath}`, async () => {
  43. if (linesALen < 10000) {
  44. return Bun.write(file, `${linesA.join('\n')}\n`);
  45. }
  46. const writer = file.writer();
  47. for (let i = 0; i < linesALen; i++) {
  48. writer.write(linesA[i]);
  49. writer.write('\n');
  50. }
  51. await writer.flush();
  52. return writer.end();
  53. });
  54. }
  55. export const withBannerArray = (title: string, description: string[], date: Date, content: string[]) => {
  56. return [
  57. '#########################################',
  58. `# ${title}`,
  59. `# Last Updated: ${date.toISOString()}`,
  60. `# Size: ${content.length}`,
  61. ...description.map(line => (line ? `# ${line}` : '#')),
  62. '#########################################',
  63. ...content,
  64. '################## EOF ##################'
  65. ];
  66. };
  67. export const createRuleset = (
  68. title: string, description: string[], date: Date, content: string[],
  69. type: 'ruleset' | 'domainset', surgePath: string, clashPath: string
  70. ) => {
  71. const surgeContent = withBannerArray(title, description, date, content);
  72. let _clashContent;
  73. switch (type) {
  74. case 'domainset':
  75. _clashContent = surgeDomainsetToClashDomainset(content);
  76. break;
  77. case 'ruleset':
  78. _clashContent = surgeRulesetToClashClassicalTextRuleset(content);
  79. break;
  80. default:
  81. throw new TypeError(`Unknown type: ${type as any}`);
  82. }
  83. const clashContent = withBannerArray(title, description, date, _clashContent);
  84. return [
  85. compareAndWriteFile(surgeContent, surgePath),
  86. compareAndWriteFile(clashContent, clashPath)
  87. ];
  88. };