output-files.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import getMeta from '../../../utils/meta'
  2. import HumanReadableLogs from '../../../ide/human-readable-logs/HumanReadableLogs'
  3. import BibLogParser from '../../../ide/log-parser/bib-log-parser'
  4. import { v4 as uuid } from 'uuid'
  5. const searchParams = new URLSearchParams(window.location.search)
  6. export function handleOutputFiles(outputFiles, projectId, data) {
  7. const result = {}
  8. const outputFile = outputFiles.get('output.pdf')
  9. if (outputFile) {
  10. // build the URL for viewing the PDF in the preview UI
  11. const params = new URLSearchParams({
  12. compileGroup: data.compileGroup,
  13. })
  14. if (data.clsiServerId) {
  15. params.set('clsiserverid', data.clsiServerId)
  16. }
  17. if (searchParams.get('verify_chunks') === 'true') {
  18. // Instruct the serviceWorker to verify composed ranges.
  19. params.set('verify_chunks', 'true')
  20. }
  21. if (getMeta('ol-enablePdfCaching')) {
  22. // Tag traffic that uses the pdf caching logic.
  23. params.set('enable_pdf_caching', 'true')
  24. }
  25. result.pdfUrl = `${buildURL(outputFile, data.pdfDownloadDomain)}?${params}`
  26. // build the URL for downloading the PDF
  27. params.set('popupDownload', 'true') // save PDF download as file
  28. result.pdfDownloadUrl = `/download/project/${projectId}/build/${outputFile.build}/output/output.pdf?${params}`
  29. }
  30. return result
  31. }
  32. export const handleLogFiles = async (outputFiles, data, signal) => {
  33. const result = {
  34. log: null,
  35. logEntries: {
  36. errors: [],
  37. warnings: [],
  38. typesetting: [],
  39. },
  40. }
  41. function accumulateResults(newEntries, type) {
  42. for (const key in result.logEntries) {
  43. if (newEntries[key]) {
  44. for (const entry of newEntries[key]) {
  45. if (type) {
  46. entry.type = newEntries.type
  47. }
  48. if (entry.file) {
  49. entry.file = normalizeFilePath(entry.file)
  50. }
  51. entry.key = uuid()
  52. }
  53. result.logEntries[key].push(...newEntries[key])
  54. }
  55. }
  56. }
  57. const logFile = outputFiles.get('output.log')
  58. if (logFile) {
  59. try {
  60. const response = await fetch(buildURL(logFile, data.pdfDownloadDomain), {
  61. signal,
  62. })
  63. result.log = await response.text()
  64. const { errors, warnings, typesetting } = HumanReadableLogs.parse(
  65. result.log,
  66. {
  67. ignoreDuplicates: true,
  68. }
  69. )
  70. accumulateResults({ errors, warnings, typesetting })
  71. } catch (e) {
  72. console.warn(e) // ignore failure to fetch/parse the log file, but log a warning
  73. }
  74. }
  75. const blgFile = outputFiles.get('output.blg')
  76. if (blgFile) {
  77. try {
  78. const response = await fetch(buildURL(blgFile, data.pdfDownloadDomain), {
  79. signal,
  80. })
  81. const log = await response.text()
  82. try {
  83. const { errors, warnings } = new BibLogParser(log, {
  84. maxErrors: 100,
  85. }).parse()
  86. accumulateResults({ errors, warnings }, 'BibTeX:')
  87. } catch (e) {
  88. // BibLog parsing errors are ignored
  89. }
  90. } catch (e) {
  91. console.warn(e) // ignore failure to fetch/parse the log file, but log a warning
  92. }
  93. }
  94. result.logEntries.all = [
  95. ...result.logEntries.errors,
  96. ...result.logEntries.warnings,
  97. ...result.logEntries.typesetting,
  98. ]
  99. return result
  100. }
  101. export function buildLogEntryAnnotations(entries, fileTreeManager) {
  102. const rootDocDirname = fileTreeManager.getRootDocDirname()
  103. const logEntryAnnotations = {}
  104. for (const entry of entries) {
  105. if (entry.file) {
  106. entry.file = normalizeFilePath(entry.file, rootDocDirname)
  107. const entity = fileTreeManager.findEntityByPath(entry.file)
  108. if (entity) {
  109. if (!(entity.id in logEntryAnnotations)) {
  110. logEntryAnnotations[entity.id] = []
  111. }
  112. logEntryAnnotations[entity.id].push({
  113. row: entry.line - 1,
  114. type: entry.level === 'error' ? 'error' : 'warning',
  115. text: entry.message,
  116. source: 'compile',
  117. })
  118. }
  119. }
  120. }
  121. return logEntryAnnotations
  122. }
  123. function buildURL(file, pdfDownloadDomain) {
  124. if (file.build && pdfDownloadDomain) {
  125. // Downloads from the compiles domain must include a build id.
  126. // The build id is used implicitly for access control.
  127. return `${pdfDownloadDomain}${file.url}`
  128. }
  129. // Go through web instead, which uses mongo for checking project access.
  130. return file.url
  131. }
  132. function normalizeFilePath(path, rootDocDirname) {
  133. path = path.replace(
  134. /^.*\/compiles\/[0-9a-f]{24}(-[0-9a-f]{24})?\/(\.\/)?/,
  135. ''
  136. )
  137. path = path.replace(/^\/compile\//, '')
  138. if (rootDocDirname) {
  139. path = path.replace(/^\.\//, rootDocDirname + '/')
  140. }
  141. return path
  142. }