enumerate-source-domains.ts 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. import { SOURCE_DIR } from '../constants/dir';
  2. import path from 'node:path';
  3. import { fdir as Fdir } from 'fdir';
  4. import runAgainstSourceFile from './run-against-source-file';
  5. export interface SourceDomain {
  6. domain: string,
  7. /**
  8. * `true` for `DOMAIN-SUFFIX` / leading-dot domainset entries (apex domain
  9. * that includes all subdomains), `false` for exact `DOMAIN` entries.
  10. */
  11. includeAllSubdomain: boolean
  12. }
  13. function sourceFileFilter(filePath: string, isDirectory: boolean) {
  14. if (isDirectory) return false;
  15. const extname = path.extname(filePath);
  16. return extname === '.txt' || extname === '.conf';
  17. }
  18. /**
  19. * Crawl the `domainset` and `non_ip` source directories and return every
  20. * domain entry they declare.
  21. *
  22. * A domain that appears with `includeAllSubdomain: true` in any source wins
  23. * over an exact-only occurrence: checking the apex (with subdomains) also
  24. * covers the exact host, so we collapse duplicates to the broader check. This
  25. * is the natural dedupe that makes sharding by domain hash correct.
  26. */
  27. export async function enumerateSourceDomains(): Promise<SourceDomain[]> {
  28. const [domainSets, domainRules] = await Promise.all([
  29. new Fdir()
  30. .withFullPaths()
  31. .filter(sourceFileFilter)
  32. .crawl(SOURCE_DIR + path.sep + 'domainset')
  33. .withPromise(),
  34. new Fdir()
  35. .withFullPaths()
  36. .filter(sourceFileFilter)
  37. .crawl(SOURCE_DIR + path.sep + 'non_ip')
  38. .withPromise()
  39. ]);
  40. // domain -> includeAllSubdomain (true wins)
  41. const seen = new Map<string, boolean>();
  42. await Promise.all(
  43. [...domainRules, ...domainSets].map(filepath => runAgainstSourceFile(
  44. filepath,
  45. (domain: string, includeAllSubdomain: boolean) => {
  46. const prev = seen.get(domain);
  47. if (prev === undefined) {
  48. seen.set(domain, includeAllSubdomain);
  49. } else if (includeAllSubdomain && !prev) {
  50. seen.set(domain, true);
  51. }
  52. }
  53. ))
  54. );
  55. const result: SourceDomain[] = [];
  56. for (const [domain, includeAllSubdomain] of seen) {
  57. result.push({ domain, includeAllSubdomain });
  58. }
  59. return result;
  60. }