local-compile-context.jsx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. import {
  2. createContext,
  3. useCallback,
  4. useContext,
  5. useEffect,
  6. useMemo,
  7. useRef,
  8. useState,
  9. } from 'react'
  10. import PropTypes from 'prop-types'
  11. import useScopeValue from '../hooks/use-scope-value'
  12. import useScopeValueSetterOnly from '../hooks/use-scope-value-setter-only'
  13. import usePersistedState from '../hooks/use-persisted-state'
  14. import useAbortController from '../hooks/use-abort-controller'
  15. import DocumentCompiler from '../../features/pdf-preview/util/compiler'
  16. import {
  17. send,
  18. sendMB,
  19. sendMBOnce,
  20. sendMBSampled,
  21. } from '../../infrastructure/event-tracking'
  22. import {
  23. buildLogEntryAnnotations,
  24. countRules,
  25. handleLogFiles,
  26. handleOutputFiles,
  27. } from '../../features/pdf-preview/util/output-files'
  28. import { useIdeContext } from './ide-context'
  29. import { useProjectContext } from './project-context'
  30. import { useEditorContext } from './editor-context'
  31. import { buildFileList } from '../../features/pdf-preview/util/file-list'
  32. import { useLayoutContext } from './layout-context'
  33. import { useUserContext } from './user-context'
  34. import { useFileTreeData } from '@/shared/context/file-tree-data-context'
  35. import { useFileTreePathContext } from '@/features/file-tree/contexts/file-tree-path'
  36. import { useUserSettingsContext } from '@/shared/context/user-settings-context'
  37. import { useSplitTestContext } from '@/shared/context/split-test-context'
  38. export const LocalCompileContext = createContext()
  39. export const CompileContextPropTypes = {
  40. value: PropTypes.shape({
  41. autoCompile: PropTypes.bool.isRequired,
  42. clearingCache: PropTypes.bool.isRequired,
  43. clsiServerId: PropTypes.string,
  44. codeCheckFailed: PropTypes.bool.isRequired,
  45. compiling: PropTypes.bool.isRequired,
  46. deliveryLatencies: PropTypes.object.isRequired,
  47. draft: PropTypes.bool.isRequired,
  48. error: PropTypes.string,
  49. fileList: PropTypes.object,
  50. hasChanges: PropTypes.bool.isRequired,
  51. highlights: PropTypes.arrayOf(PropTypes.object),
  52. logEntries: PropTypes.object,
  53. logEntryAnnotations: PropTypes.object,
  54. pdfDownloadUrl: PropTypes.string,
  55. pdfFile: PropTypes.object,
  56. pdfUrl: PropTypes.string,
  57. pdfViewer: PropTypes.string,
  58. position: PropTypes.object,
  59. rawLog: PropTypes.string,
  60. setAutoCompile: PropTypes.func.isRequired,
  61. setDraft: PropTypes.func.isRequired,
  62. setError: PropTypes.func.isRequired,
  63. setHasLintingError: PropTypes.func.isRequired, // only for storybook
  64. setHighlights: PropTypes.func.isRequired,
  65. setPosition: PropTypes.func.isRequired,
  66. setShowCompileTimeWarning: PropTypes.func.isRequired,
  67. setShowLogs: PropTypes.func.isRequired,
  68. toggleLogs: PropTypes.func.isRequired,
  69. setStopOnFirstError: PropTypes.func.isRequired,
  70. setStopOnValidationError: PropTypes.func.isRequired,
  71. showCompileTimeWarning: PropTypes.bool.isRequired,
  72. showLogs: PropTypes.bool.isRequired,
  73. showNewCompileTimeoutUI: PropTypes.string,
  74. showFasterCompilesFeedbackUI: PropTypes.bool.isRequired,
  75. stopOnFirstError: PropTypes.bool.isRequired,
  76. stopOnValidationError: PropTypes.bool.isRequired,
  77. stoppedOnFirstError: PropTypes.bool.isRequired,
  78. uncompiled: PropTypes.bool,
  79. validationIssues: PropTypes.object,
  80. firstRenderDone: PropTypes.func.isRequired,
  81. cleanupCompileResult: PropTypes.func,
  82. }),
  83. }
  84. LocalCompileContext.Provider.propTypes = CompileContextPropTypes
  85. export function LocalCompileProvider({ children }) {
  86. const ide = useIdeContext()
  87. const { hasPremiumCompile, isProjectOwner } = useEditorContext()
  88. const {
  89. _id: projectId,
  90. rootDocId,
  91. showNewCompileTimeoutUI,
  92. } = useProjectContext()
  93. const { pdfPreviewOpen } = useLayoutContext()
  94. const { features } = useUserContext()
  95. const { fileTreeData } = useFileTreeData()
  96. const { findEntityByPath } = useFileTreePathContext()
  97. // whether a compile is in progress
  98. const [compiling, setCompiling] = useState(false)
  99. // whether to show the compile time warning
  100. const [showCompileTimeWarning, setShowCompileTimeWarning] = useState(false)
  101. // the log entries parsed from the compile output log
  102. const [logEntries, setLogEntries] = useScopeValueSetterOnly('pdf.logEntries')
  103. // annotations for display in the editor, built from the log entries
  104. const [logEntryAnnotations, setLogEntryAnnotations] = useScopeValue(
  105. 'pdf.logEntryAnnotations'
  106. )
  107. // the PDF viewer and whether syntax validation is enabled globally
  108. const { userSettings } = useUserSettingsContext()
  109. const { pdfViewer, syntaxValidation } = userSettings
  110. // the URL for downloading the PDF
  111. const [, setPdfDownloadUrl] = useScopeValueSetterOnly('pdf.downloadUrl')
  112. // the URL for loading the PDF in the preview pane
  113. const [, setPdfUrl] = useScopeValueSetterOnly('pdf.url')
  114. // low level details for metrics
  115. const [pdfFile, setPdfFile] = useState()
  116. useEffect(() => {
  117. setPdfDownloadUrl(pdfFile?.pdfDownloadUrl)
  118. setPdfUrl(pdfFile?.pdfUrl)
  119. }, [pdfFile, setPdfDownloadUrl, setPdfUrl])
  120. // the project is considered to be "uncompiled" if a doc has changed, or finished saving, since the last compile started.
  121. const [uncompiled, setUncompiled] = useScopeValue('pdf.uncompiled')
  122. // whether a doc has been edited since the last compile started
  123. const [editedSinceCompileStarted, setEditedSinceCompileStarted] =
  124. useState(false)
  125. // the id of the CLSI server which ran the compile
  126. const [clsiServerId, setClsiServerId] = useState()
  127. // data received in response to a compile request
  128. const [data, setData] = useState()
  129. // the rootDocId used in the most recent compile request, which may not be the
  130. // same as the project rootDocId. This is used to calculate correct paths when
  131. // parsing the compile logs
  132. const lastCompileRootDocId = data?.rootDocId
  133. // callback to be invoked for PdfJsMetrics
  134. const [firstRenderDone, setFirstRenderDone] = useState(() => () => {})
  135. // latencies of compile/pdf download/rendering
  136. const [deliveryLatencies, setDeliveryLatencies] = useState({})
  137. // whether the project has been compiled yet
  138. const [compiledOnce, setCompiledOnce] = useState(false)
  139. // whether the cache is being cleared
  140. const [clearingCache, setClearingCache] = useState(false)
  141. // whether the logs should be visible
  142. const [showLogs, setShowLogs] = useState(false)
  143. // whether the faster compiles feedback UI should be displayed
  144. const [showFasterCompilesFeedbackUI, setShowFasterCompilesFeedbackUI] =
  145. useState(false)
  146. // whether the compile dropdown arrow should be animated
  147. const [animateCompileDropdownArrow, setAnimateCompileDropdownArrow] =
  148. useState(false)
  149. const toggleLogs = useCallback(() => {
  150. setShowLogs(prev => {
  151. if (!prev) {
  152. sendMBOnce('ide-open-logs-once')
  153. }
  154. return !prev
  155. })
  156. }, [setShowLogs])
  157. // an error that occurred
  158. const [error, setError] = useState()
  159. // the list of files that can be downloaded
  160. const [fileList, setFileList] = useState()
  161. // the raw contents of the log file
  162. const [rawLog, setRawLog] = useState()
  163. // validation issues from CLSI
  164. const [validationIssues, setValidationIssues] = useState()
  165. // areas to highlight on the PDF, from synctex
  166. const [highlights, setHighlights] = useState()
  167. // scroll position of the PDF
  168. const [position, setPosition] = usePersistedState(`pdf.position.${projectId}`)
  169. // whether autocompile is switched on
  170. const [autoCompile, setAutoCompile] = usePersistedState(
  171. `autocompile_enabled:${projectId}`,
  172. false,
  173. true
  174. )
  175. // whether the compile should run in draft mode
  176. const [draft, setDraft] = usePersistedState(`draft:${projectId}`, false, true)
  177. // whether compiling should stop on first error
  178. const [stopOnFirstError, setStopOnFirstError] = usePersistedState(
  179. `stop_on_first_error:${projectId}`,
  180. false,
  181. true
  182. )
  183. // whether the last compiles stopped on first error
  184. const [stoppedOnFirstError, setStoppedOnFirstError] = useState(false)
  185. // whether compiling should be prevented if there are linting errors
  186. const [stopOnValidationError, setStopOnValidationError] = usePersistedState(
  187. `stop_on_validation_error:${projectId}`,
  188. true,
  189. true
  190. )
  191. // the Document currently open in the editor
  192. const [currentDoc] = useScopeValue('editor.sharejs_doc')
  193. // whether the editor linter found errors
  194. const [hasLintingError, setHasLintingError] = useScopeValue('hasLintingError')
  195. // the timestamp that a doc was last changed
  196. const [changedAt, setChangedAt] = useState(0)
  197. // the timestamp that a doc was last saved
  198. const [savedAt, setSavedAt] = useState(0)
  199. const { signal } = useAbortController()
  200. const cleanupCompileResult = useCallback(() => {
  201. setPdfFile(null)
  202. setLogEntries(null)
  203. setLogEntryAnnotations({})
  204. }, [setPdfFile, setLogEntries, setLogEntryAnnotations])
  205. const compilingRef = useRef(false)
  206. useEffect(() => {
  207. compilingRef.current = compiling
  208. }, [compiling])
  209. const _buildLogEntryAnnotations = useCallback(
  210. entries =>
  211. buildLogEntryAnnotations(entries, fileTreeData, lastCompileRootDocId),
  212. [fileTreeData, lastCompileRootDocId]
  213. )
  214. const buildLogEntryAnnotationsRef = useRef(_buildLogEntryAnnotations)
  215. useEffect(() => {
  216. buildLogEntryAnnotationsRef.current = _buildLogEntryAnnotations
  217. }, [_buildLogEntryAnnotations])
  218. // the document compiler
  219. const [compiler] = useState(() => {
  220. return new DocumentCompiler({
  221. projectId,
  222. setChangedAt,
  223. setSavedAt,
  224. setCompiling,
  225. setData,
  226. setFirstRenderDone,
  227. setDeliveryLatencies,
  228. setError,
  229. cleanupCompileResult,
  230. compilingRef,
  231. signal,
  232. })
  233. })
  234. // keep currentDoc in sync with the compiler
  235. useEffect(() => {
  236. compiler.currentDoc = currentDoc
  237. }, [compiler, currentDoc])
  238. // keep the project rootDocId in sync with the compiler
  239. useEffect(() => {
  240. compiler.projectRootDocId = rootDocId
  241. }, [compiler, rootDocId])
  242. // keep draft setting in sync with the compiler
  243. useEffect(() => {
  244. compiler.setOption('draft', draft)
  245. }, [compiler, draft])
  246. // keep stop on first error setting in sync with the compiler
  247. useEffect(() => {
  248. compiler.setOption('stopOnFirstError', stopOnFirstError)
  249. }, [compiler, stopOnFirstError])
  250. useEffect(() => {
  251. setUncompiled(changedAt > 0 || savedAt > 0)
  252. }, [setUncompiled, changedAt, savedAt])
  253. useEffect(() => {
  254. setEditedSinceCompileStarted(changedAt > 0)
  255. }, [setEditedSinceCompileStarted, changedAt])
  256. // always compile the PDF once after opening the project, after the doc has loaded
  257. useEffect(() => {
  258. if (!compiledOnce && currentDoc) {
  259. setCompiledOnce(true)
  260. compiler.compile({ isAutoCompileOnLoad: true })
  261. }
  262. }, [compiledOnce, currentDoc, compiler])
  263. useEffect(() => {
  264. const compileTimeWarningEnabled = features?.compileTimeout <= 60
  265. if (compileTimeWarningEnabled && compiling && isProjectOwner) {
  266. const timeout = window.setTimeout(() => {
  267. setShowCompileTimeWarning(true)
  268. }, 30000)
  269. return () => {
  270. window.clearTimeout(timeout)
  271. }
  272. }
  273. }, [compiling, isProjectOwner, features])
  274. const { splitTestVariants } = useSplitTestContext()
  275. // handle the data returned from a compile request
  276. // note: this should _only_ run when `data` changes,
  277. // the other dependencies must all be static
  278. useEffect(() => {
  279. const abortController = new AbortController()
  280. if (data) {
  281. if (data.clsiServerId) {
  282. setClsiServerId(data.clsiServerId) // set in scope, for PdfSynctexController
  283. }
  284. setShowFasterCompilesFeedbackUI(
  285. Boolean(data.showFasterCompilesFeedbackUI)
  286. )
  287. if (data.outputFiles) {
  288. const outputFiles = new Map()
  289. for (const outputFile of data.outputFiles) {
  290. outputFiles.set(outputFile.path, outputFile)
  291. }
  292. // set the PDF context
  293. if (data.status === 'success') {
  294. setPdfFile(handleOutputFiles(outputFiles, projectId, data))
  295. }
  296. setFileList(
  297. buildFileList(outputFiles, data.clsiServerId, data.compileGroup)
  298. )
  299. // handle log files
  300. // asynchronous (TODO: cancel on new compile?)
  301. setLogEntryAnnotations(null)
  302. setLogEntries(null)
  303. setRawLog(null)
  304. handleLogFiles(outputFiles, data, abortController.signal).then(
  305. result => {
  306. setRawLog(result.log)
  307. setLogEntries(result.logEntries)
  308. setLogEntryAnnotations(
  309. buildLogEntryAnnotationsRef.current(result.logEntries.all)
  310. )
  311. // sample compile stats for real users
  312. if (!window.user.alphaProgram) {
  313. if (['success', 'stopped-on-first-error'].includes(data.status)) {
  314. sendMBSampled(
  315. 'compile-result',
  316. {
  317. errors: result.logEntries.errors.length,
  318. warnings: result.logEntries.warnings.length,
  319. typesetting: result.logEntries.typesetting.length,
  320. newPdfPreview: true, // TODO: is this useful?
  321. stopOnFirstError: data.options.stopOnFirstError,
  322. },
  323. 0.01
  324. )
  325. }
  326. if (splitTestVariants['compile-log-events'] === 'enabled') {
  327. sendMB('compile-log-entries', {
  328. status: data.status,
  329. stopOnFirstError: data.options.stopOnFirstError,
  330. isAutoCompileOnLoad: !!data.options.isAutoCompileOnLoad,
  331. isAutoCompileOnChange: !!data.options.isAutoCompileOnChange,
  332. errors: countRules(result.logEntries.errors),
  333. warnings: countRules(result.logEntries.warnings),
  334. typesetting: countRules(result.logEntries.typesetting),
  335. })
  336. }
  337. }
  338. }
  339. )
  340. }
  341. switch (data.status) {
  342. case 'success':
  343. setError(undefined)
  344. setShowLogs(false)
  345. break
  346. case 'stopped-on-first-error':
  347. setError(undefined)
  348. setShowLogs(true)
  349. break
  350. case 'clsi-maintenance':
  351. case 'compile-in-progress':
  352. case 'exited':
  353. case 'failure':
  354. case 'project-too-large':
  355. case 'rate-limited':
  356. case 'terminated':
  357. case 'too-recently-compiled':
  358. setError(data.status)
  359. break
  360. case 'timedout':
  361. setError('timedout')
  362. if (!hasPremiumCompile && isProjectOwner) {
  363. send(
  364. 'subscription-funnel',
  365. 'editor-click-feature',
  366. 'compile-timeout'
  367. )
  368. }
  369. break
  370. case 'autocompile-backoff':
  371. if (!data.options.isAutoCompileOnLoad) {
  372. setError('autocompile-disabled')
  373. setAutoCompile(false)
  374. }
  375. break
  376. case 'unavailable':
  377. setError('clsi-unavailable')
  378. break
  379. case 'validation-problems':
  380. setError('validation-problems')
  381. setValidationIssues(data.validationProblems)
  382. break
  383. default:
  384. setError('error')
  385. break
  386. }
  387. setStoppedOnFirstError(data.status === 'stopped-on-first-error')
  388. }
  389. return () => {
  390. abortController.abort()
  391. }
  392. }, [
  393. data,
  394. ide,
  395. hasPremiumCompile,
  396. isProjectOwner,
  397. projectId,
  398. setAutoCompile,
  399. setClsiServerId,
  400. setLogEntries,
  401. setLogEntryAnnotations,
  402. setPdfFile,
  403. splitTestVariants,
  404. ])
  405. // switch to logs if there's an error
  406. useEffect(() => {
  407. if (error) {
  408. setShowLogs(true)
  409. }
  410. }, [error])
  411. // whether there has been an autocompile linting error, if syntax validation is switched on
  412. const autoCompileLintingError = Boolean(
  413. autoCompile && syntaxValidation && hasLintingError
  414. )
  415. const codeCheckFailed = stopOnValidationError && autoCompileLintingError
  416. // the project is available for auto-compiling
  417. // (autocompile is enabled, the PDF preview is open, and the code check (if enabled) hasn't failed)
  418. const canAutoCompile = Boolean(
  419. autoCompile && pdfPreviewOpen && !codeCheckFailed
  420. )
  421. // show that the project has pending changes
  422. const hasChanges = Boolean(canAutoCompile && uncompiled && compiledOnce)
  423. // call the debounced autocompile function if the project is available for auto-compiling and it has changed
  424. useEffect(() => {
  425. if (canAutoCompile) {
  426. if (changedAt > 0 || savedAt > 0) {
  427. compiler.debouncedAutoCompile()
  428. }
  429. } else {
  430. compiler.debouncedAutoCompile.cancel()
  431. }
  432. }, [compiler, canAutoCompile, changedAt, savedAt])
  433. // cancel debounced recompile on unmount
  434. useEffect(() => {
  435. return () => {
  436. compiler.debouncedAutoCompile.cancel()
  437. }
  438. }, [compiler])
  439. // start a compile manually
  440. const startCompile = useCallback(
  441. options => {
  442. compiler.compile(options)
  443. },
  444. [compiler]
  445. )
  446. // stop a compile manually
  447. const stopCompile = useCallback(() => {
  448. compiler.stopCompile()
  449. }, [compiler])
  450. // clear the compile cache
  451. const clearCache = useCallback(() => {
  452. setClearingCache(true)
  453. return compiler
  454. .clearCache()
  455. .then(() => {
  456. setFileList(undefined)
  457. setPdfFile(undefined)
  458. })
  459. .finally(() => {
  460. setClearingCache(false)
  461. })
  462. }, [compiler])
  463. const syncToEntry = useCallback(
  464. entry => {
  465. const result = findEntityByPath(entry.file)
  466. if (result && result.type === 'doc') {
  467. ide.editorManager.openDocId(result.entity._id, {
  468. gotoLine: entry.line ?? undefined,
  469. gotoColumn: entry.column ?? undefined,
  470. })
  471. }
  472. },
  473. [findEntityByPath, ide.editorManager]
  474. )
  475. // clear the cache then run a compile, triggered by a menu item
  476. const recompileFromScratch = useCallback(() => {
  477. clearCache().then(() => {
  478. compiler.compile()
  479. })
  480. }, [clearCache, compiler])
  481. // After a compile, the compiler sets `data.options` to the options that were
  482. // used for that compile.
  483. const lastCompileOptions = useMemo(() => data?.options || {}, [data])
  484. const value = useMemo(
  485. () => ({
  486. animateCompileDropdownArrow,
  487. autoCompile,
  488. clearCache,
  489. clearingCache,
  490. clsiServerId,
  491. codeCheckFailed,
  492. compiling,
  493. deliveryLatencies,
  494. draft,
  495. editedSinceCompileStarted,
  496. error,
  497. fileList,
  498. hasChanges,
  499. highlights,
  500. isProjectOwner,
  501. lastCompileOptions,
  502. logEntryAnnotations,
  503. logEntries,
  504. pdfDownloadUrl: pdfFile?.pdfDownloadUrl,
  505. pdfFile,
  506. pdfUrl: pdfFile?.pdfUrl,
  507. pdfViewer,
  508. position,
  509. rawLog,
  510. recompileFromScratch,
  511. setAnimateCompileDropdownArrow,
  512. setAutoCompile,
  513. setCompiling,
  514. setDraft,
  515. setError,
  516. setHasLintingError, // only for stories
  517. setHighlights,
  518. setPosition,
  519. showCompileTimeWarning,
  520. setShowCompileTimeWarning,
  521. setShowLogs,
  522. toggleLogs,
  523. setStopOnFirstError,
  524. setStopOnValidationError,
  525. showLogs,
  526. showNewCompileTimeoutUI,
  527. showFasterCompilesFeedbackUI,
  528. startCompile,
  529. stopCompile,
  530. stopOnFirstError,
  531. stopOnValidationError,
  532. stoppedOnFirstError,
  533. uncompiled,
  534. validationIssues,
  535. firstRenderDone,
  536. setChangedAt,
  537. setSavedAt,
  538. cleanupCompileResult,
  539. syncToEntry,
  540. }),
  541. [
  542. animateCompileDropdownArrow,
  543. autoCompile,
  544. clearCache,
  545. clearingCache,
  546. clsiServerId,
  547. codeCheckFailed,
  548. compiling,
  549. deliveryLatencies,
  550. draft,
  551. editedSinceCompileStarted,
  552. error,
  553. fileList,
  554. hasChanges,
  555. highlights,
  556. isProjectOwner,
  557. lastCompileOptions,
  558. logEntries,
  559. logEntryAnnotations,
  560. position,
  561. pdfFile,
  562. pdfViewer,
  563. rawLog,
  564. recompileFromScratch,
  565. setAnimateCompileDropdownArrow,
  566. setAutoCompile,
  567. setDraft,
  568. setError,
  569. setHasLintingError, // only for stories
  570. setHighlights,
  571. setPosition,
  572. setShowCompileTimeWarning,
  573. setStopOnFirstError,
  574. setStopOnValidationError,
  575. showCompileTimeWarning,
  576. showLogs,
  577. showNewCompileTimeoutUI,
  578. showFasterCompilesFeedbackUI,
  579. startCompile,
  580. stopCompile,
  581. stopOnFirstError,
  582. stopOnValidationError,
  583. stoppedOnFirstError,
  584. uncompiled,
  585. validationIssues,
  586. firstRenderDone,
  587. setChangedAt,
  588. setSavedAt,
  589. cleanupCompileResult,
  590. setShowLogs,
  591. toggleLogs,
  592. syncToEntry,
  593. ]
  594. )
  595. return (
  596. <LocalCompileContext.Provider value={value}>
  597. {children}
  598. </LocalCompileContext.Provider>
  599. )
  600. }
  601. LocalCompileProvider.propTypes = {
  602. children: PropTypes.any,
  603. }
  604. export function useLocalCompileContext(propTypes) {
  605. const data = useContext(LocalCompileContext)
  606. PropTypes.checkPropTypes(
  607. propTypes,
  608. data,
  609. 'data',
  610. 'LocalCompileContext.Provider'
  611. )
  612. return data
  613. }