fingerprintjs

Форк
0
/
make_selectors_tester.ts 
119 строк · 3.5 Кб
1
/*
2
 * See docs/content_blockers.md
3
 */
4

5
import * as path from 'path'
6
import { promises as fsAsync } from 'fs'
7
import * as rollup from 'rollup'
8
import rollupConfig from '../../rollup.config'
9
import filterConfig, { FilterList } from './filters'
10
import { fetchFilter } from './utils'
11

12
const inputScript = path.join(__dirname, 'selectors_tester.ts')
13
const outputFile = path.join(__dirname, 'selectors_tester.html')
14

15
run()
16

17
async function run() {
18
  const uniqueSelectors = await fetchUniqueSelectors(filterConfig)
19
  const testerHtml = await makeTesterHtml(uniqueSelectors)
20
  await fsAsync.writeFile(outputFile, testerHtml)
21
}
22

23
async function fetchUniqueSelectors(filterConfig: FilterList) {
24
  const filters = Object.values(filterConfig)
25
  const uniqueSelectors = new Set<string>()
26
  let fetchedFiltersCount = 0
27

28
  const clearProgress = () => {
29
    process.stdout.clearLine(0)
30
    process.stdout.cursorTo(0)
31
  }
32
  const printProgress = () => {
33
    clearProgress()
34
    process.stdout.write(`Fetching filters: ${fetchedFiltersCount} of ${filters.length}`)
35
  }
36

37
  printProgress()
38

39
  let abort: (() => void) | undefined
40
  const abortPromise = new Promise<void>((resolve) => (abort = resolve))
41
  try {
42
    await Promise.all(
43
      filters.map(async (filter) => {
44
        let filterLines: string[]
45
        try {
46
          filterLines = await fetchFilter(filter.file, abortPromise)
47
        } catch (error) {
48
          throw new Error(`Failed to fetch filter "${filter.title}" (${filter.file}): ${error}`)
49
        }
50
        for (const line of filterLines) {
51
          const selector = getSelectorFromFilterRule(line)
52
          if (selector) {
53
            uniqueSelectors.add(selector)
54
          }
55
        }
56
        ++fetchedFiltersCount
57
        printProgress()
58
      }),
59
    )
60
  } finally {
61
    abort?.()
62
    clearProgress()
63
  }
64

65
  return uniqueSelectors
66
}
67

68
function getSelectorFromFilterRule(rule: string): string | undefined {
69
  const selectorMatch = /^##(.+)$/.exec(rule)
70
  if (!selectorMatch) {
71
    return
72
  }
73
  const selector = selectorMatch[1]
74
  // Leaves only selectors suitable for `parseSimpleCssSelector` and `offsetParent` usage
75
  if (/(^embed([^\w-]|$)|\\|\[src.*=|\[style\W?=[^[]*\bposition:\s*fixed\b|\[[^\]]*\[)/i.test(selector)) {
76
    return
77
  }
78
  // Exclude iframes because they produce unwanted side effects
79
  if (/^iframe([^\w-]|$)/i.test(selector)) {
80
    return
81
  }
82
  const selectorWithoutAttributes = selector.trim().replace(/\[.*?\]/g, '[]')
83
  if (/[\s:]/.test(selectorWithoutAttributes)) {
84
    return
85
  }
86
  return selector
87
}
88

89
async function makeTesterHtml(selectors: { forEach: (callback: (selector: string) => void) => void }) {
90
  const selectorsList: string[] = []
91
  selectors.forEach((selector) => selectorsList.push(selector))
92
  const jsCode = await getJsToDetectBlockedSelectors(selectorsList)
93
  return `<!DOCTYPE html>
94
<html>
95
<head>
96
  <meta charset="utf-8" />
97
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
98
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
99
  <title>Selector blockers tester</title>
100
</head>
101
<body>
102
  <script>
103
    ${jsCode}
104
  </script>
105
</body>
106
</html>`
107
}
108

109
async function getJsToDetectBlockedSelectors(selectors: readonly string[]) {
110
  // The first configuration from rollup.config.ts is supposed to make a JS file with dependencies included
111
  const bundle = await rollup.rollup({
112
    input: inputScript,
113
    plugins: rollupConfig[0].plugins,
114
  })
115
  const { output } = await bundle.generate({
116
    format: 'iife',
117
  })
118
  return output[0].code.replace(/\[\s*\/\*\s*selectors\s*\*\/\s*]/g, JSON.stringify(selectors))
119
}
120

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.