checkSanitizeOptions.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. const crypto = require('crypto')
  2. const fs = require('fs')
  3. const Path = require('path')
  4. const cheerio = require('cheerio')
  5. const prettier = require('prettier')
  6. const sanitizeHtml = require('sanitize-html')
  7. const {
  8. sanitizeOptions,
  9. } = require('../../../modules/learn/app/src/sanitizeOptions')
  10. const EXTRACT_STYLE = process.env.EXTRACT_STYLES === 'true'
  11. const OMIT_STYLE = process.env.OMIT_STYLE !== 'false'
  12. const DUMP_CSS_IN = Path.join(
  13. Path.dirname(Path.dirname(Path.dirname(__dirname))),
  14. 'data',
  15. 'dumpFolder'
  16. )
  17. function hash(blob) {
  18. return crypto.createHash('sha1').update(blob).digest('hex')
  19. }
  20. function normalize(blob, title) {
  21. // styles are dropped in web and kept in wiki pages for previewing there.
  22. blob = blob.replace(/<style>(.+?)<\/style>/gs, (_, match) => {
  23. if (EXTRACT_STYLE) {
  24. // normalize css with prettier
  25. const css = prettier.format(match, { parser: 'css' })
  26. fs.writeFileSync(
  27. Path.join(DUMP_CSS_IN, `${hash(css)}-${encodeURIComponent(title)}.css`),
  28. `/* title: ${title} */\n\n${css}`
  29. )
  30. }
  31. if (OMIT_STYLE) {
  32. return ''
  33. }
  34. return match
  35. })
  36. // strip comments:
  37. // - comment at the bottom of each page
  38. blob = blob.replace(/<!-- \nNewPP limit report.+/s, '')
  39. // - annotation of math characters
  40. blob = blob.replace(/<!-- . -->/g, '')
  41. // wrap for consistent rendering
  42. if (blob.indexOf('<html><head>') !== 0) {
  43. blob = `<html><head>${blob}</head></html>`
  44. }
  45. // normalize inline style:
  46. // - drop trailing ;
  47. blob = blob.replace(/style="([^"]+);"/g, (_, style) => `style="${style}"`)
  48. // - normalize whitespace
  49. blob = blob.replace(
  50. /style="([^"]+)"/g,
  51. (_, style) => `style="${style.trim().replace(/([:;])\s+/g, '$1')}"`
  52. )
  53. // let cherrio do another pass
  54. return cheerio.load(blob).html()
  55. }
  56. function toText(blob) {
  57. return cheerio.load(blob).text()
  58. }
  59. const zoomOut = 50
  60. function peak(content, offset) {
  61. // show some more content before/after the mismatch
  62. if (offset > zoomOut) {
  63. offset -= zoomOut
  64. }
  65. // wrap in JSON to escape new line characters
  66. return JSON.stringify(content.slice(offset, offset + chunkSize + 2 * zoomOut))
  67. }
  68. const chunkSize = 100
  69. function findFirstMismatch(a, b) {
  70. if (a === b) return a.length
  71. let i = 0
  72. while (
  73. a.length > chunkSize &&
  74. b.length > chunkSize &&
  75. a.slice(0, chunkSize) === b.slice(0, chunkSize)
  76. ) {
  77. i++
  78. a = a.slice(chunkSize)
  79. b = b.slice(chunkSize)
  80. }
  81. return i * chunkSize
  82. }
  83. function checkSanitizeOptions(page, title, text) {
  84. text = normalize(text, title)
  85. const sanitized = normalize(sanitizeHtml(text, sanitizeOptions))
  86. if (text === sanitized) return
  87. const offset = findFirstMismatch(text, sanitized)
  88. const textToText = toText(text)
  89. const sanitizedToText = toText(sanitized)
  90. const offsetText = findFirstMismatch(textToText, sanitizedToText)
  91. console.error('---')
  92. console.error('page :', page)
  93. console.error('title :', title)
  94. console.error('match :', text === sanitized)
  95. console.error('toText :', toText(text) === toText(sanitized))
  96. console.error('text :', peak(text, offset))
  97. console.error('sanitized :', peak(sanitized, offset))
  98. console.error('textToText :', peak(textToText, offsetText))
  99. console.error('sanitizedToText:', peak(sanitizedToText, offsetText))
  100. }
  101. module.exports = {
  102. checkSanitizeOptions,
  103. }