misc.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import path, { dirname } from 'node:path';
  2. import fs from 'node:fs';
  3. import fsp from 'node:fs/promises';
  4. import { OUTPUT_CLASH_DIR, OUTPUT_SINGBOX_DIR, OUTPUT_SURGE_DIR } from '../constants/dir';
  5. import type { HeadersInit } from 'undici';
  6. export const isTruthy = <T>(i: T | 0 | '' | false | null | undefined): i is T => !!i;
  7. export function fastStringArrayJoin(arr: string[], sep: string) {
  8. const len = arr.length;
  9. if (len === 0) {
  10. return '';
  11. }
  12. let result = arr[0];
  13. for (let i = 1; i < len; i++) {
  14. result += sep;
  15. result += arr[i];
  16. }
  17. return result;
  18. }
  19. interface Write {
  20. (
  21. destination: string,
  22. input: NodeJS.TypedArray | string,
  23. ): Promise<unknown>
  24. }
  25. export function mkdirp(dir: string) {
  26. if (fs.existsSync(dir)) {
  27. return;
  28. }
  29. return fsp.mkdir(dir, { recursive: true });
  30. }
  31. export const writeFile: Write = async (destination: string, input, dir = dirname(destination)) => {
  32. const p = mkdirp(dir);
  33. if (p) {
  34. await p;
  35. }
  36. return fsp.writeFile(destination, input, { encoding: 'utf-8' });
  37. };
  38. export const removeFiles = async (files: string[]) => Promise.all(files.map((file) => fsp.rm(file, { force: true })));
  39. export function domainWildCardToRegex(domain: string) {
  40. let result = '^';
  41. for (let i = 0, len = domain.length; i < len; i++) {
  42. switch (domain[i]) {
  43. case '.':
  44. result += String.raw`\.`;
  45. break;
  46. case '*':
  47. result += '[a-zA-Z0-9-_.]*?';
  48. break;
  49. case '?':
  50. result += '[a-zA-Z0-9-_.]';
  51. break;
  52. default:
  53. result += domain[i];
  54. }
  55. }
  56. result += '$';
  57. return result;
  58. }
  59. export const identity = <T>(x: T): T => x;
  60. export function appendArrayFromSet<T>(dest: T[], source: Set<T> | Array<Set<T>>, transformer: (item: T) => T = identity) {
  61. const casted = Array.isArray(source) ? source : [source];
  62. for (let i = 0, len = casted.length; i < len; i++) {
  63. const iterator = casted[i].values();
  64. let step: IteratorResult<T, undefined>;
  65. while ((step = iterator.next(), !step.done)) {
  66. dest.push(transformer(step.value));
  67. }
  68. }
  69. return dest;
  70. }
  71. export function output(id: string, type: 'non_ip' | 'ip' | 'domainset') {
  72. return [
  73. path.join(OUTPUT_SURGE_DIR, type, id + '.conf'),
  74. path.join(OUTPUT_CLASH_DIR, type, id + '.txt'),
  75. path.join(OUTPUT_SINGBOX_DIR, type, id + '.json')
  76. ] as const;
  77. }
  78. export function withBannerArray(title: string, description: string[] | readonly string[], date: Date, content: string[]) {
  79. return [
  80. '#########################################',
  81. `# ${title}`,
  82. `# Last Updated: ${date.toISOString()}`,
  83. `# Size: ${content.length}`,
  84. ...description.map(line => (line ? `# ${line}` : '#')),
  85. '#########################################',
  86. ...content,
  87. '################## EOF ##################'
  88. ];
  89. };
  90. export function mergeHeaders<T extends RequestInit['headers'] | HeadersInit>(headersA: T | undefined, headersB: T): T {
  91. if (headersA == null) {
  92. return headersB;
  93. }
  94. if (Array.isArray(headersB)) {
  95. throw new TypeError('Array headers is not supported');
  96. }
  97. const result = new Headers(headersA as any);
  98. if (headersB instanceof Headers) {
  99. headersB.forEach((value, key) => {
  100. result.set(key, value);
  101. });
  102. return result as T;
  103. }
  104. for (const key in headersB) {
  105. if (Object.hasOwn(headersB, key)) {
  106. result.set(key, (headersB as Record<string, any>)[key]);
  107. }
  108. }
  109. return result as T;
  110. }