|
@@ -1,27 +1,72 @@
|
|
|
-import { createContext, useContext, useMemo } from 'react'
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ createContext,
|
|
|
|
|
+ useCallback,
|
|
|
|
|
+ useContext,
|
|
|
|
|
+ useEffect,
|
|
|
|
|
+ useMemo,
|
|
|
|
|
+ useState,
|
|
|
|
|
+} from 'react'
|
|
|
import PropTypes from 'prop-types'
|
|
import PropTypes from 'prop-types'
|
|
|
import useScopeValue from '../hooks/use-scope-value'
|
|
import useScopeValue from '../hooks/use-scope-value'
|
|
|
|
|
+import usePersistedState from '../hooks/use-persisted-state'
|
|
|
|
|
+import useAbortController from '../hooks/use-abort-controller'
|
|
|
|
|
+import DocumentCompiler from '../../features/pdf-preview/util/compiler'
|
|
|
|
|
+import {
|
|
|
|
|
+ send,
|
|
|
|
|
+ sendMB,
|
|
|
|
|
+ sendMBSampled,
|
|
|
|
|
+} from '../../infrastructure/event-tracking'
|
|
|
|
|
+import {
|
|
|
|
|
+ buildLogEntryAnnotations,
|
|
|
|
|
+ handleOutputFiles,
|
|
|
|
|
+} from '../../features/pdf-preview/util/output-files'
|
|
|
|
|
+import { useIdeContext } from './ide-context'
|
|
|
|
|
+import { useProjectContext } from './project-context'
|
|
|
|
|
+import { useEditorContext } from './editor-context'
|
|
|
|
|
|
|
|
export const CompileContext = createContext()
|
|
export const CompileContext = createContext()
|
|
|
|
|
|
|
|
CompileContext.Provider.propTypes = {
|
|
CompileContext.Provider.propTypes = {
|
|
|
value: PropTypes.shape({
|
|
value: PropTypes.shape({
|
|
|
|
|
+ autoCompile: PropTypes.bool.isRequired,
|
|
|
|
|
+ clearingCache: PropTypes.bool.isRequired,
|
|
|
clsiServerId: PropTypes.string,
|
|
clsiServerId: PropTypes.string,
|
|
|
|
|
+ codeCheckFailed: PropTypes.bool.isRequired,
|
|
|
|
|
+ compiling: PropTypes.bool.isRequired,
|
|
|
|
|
+ draft: PropTypes.bool.isRequired,
|
|
|
|
|
+ error: PropTypes.string,
|
|
|
|
|
+ fileList: PropTypes.object,
|
|
|
|
|
+ hasChanges: PropTypes.bool.isRequired,
|
|
|
|
|
+ hasLintingError: PropTypes.bool,
|
|
|
logEntries: PropTypes.object,
|
|
logEntries: PropTypes.object,
|
|
|
logEntryAnnotations: PropTypes.object,
|
|
logEntryAnnotations: PropTypes.object,
|
|
|
pdfDownloadUrl: PropTypes.string,
|
|
pdfDownloadUrl: PropTypes.string,
|
|
|
pdfUrl: PropTypes.string,
|
|
pdfUrl: PropTypes.string,
|
|
|
- setClsiServerId: PropTypes.func.isRequired,
|
|
|
|
|
- setLogEntries: PropTypes.func.isRequired,
|
|
|
|
|
- setLogEntryAnnotations: PropTypes.func.isRequired,
|
|
|
|
|
- setPdfDownloadUrl: PropTypes.func.isRequired,
|
|
|
|
|
- setPdfUrl: PropTypes.func.isRequired,
|
|
|
|
|
- setUncompiled: PropTypes.func.isRequired,
|
|
|
|
|
|
|
+ rawLog: PropTypes.string,
|
|
|
|
|
+ setAutoCompile: PropTypes.func.isRequired,
|
|
|
|
|
+ setDraft: PropTypes.func.isRequired,
|
|
|
|
|
+ setHasLintingError: PropTypes.func.isRequired, // only for storybook
|
|
|
|
|
+ setShowLogs: PropTypes.func.isRequired,
|
|
|
|
|
+ setStopOnValidationError: PropTypes.func.isRequired,
|
|
|
|
|
+ showLogs: PropTypes.bool.isRequired,
|
|
|
|
|
+ stopOnValidationError: PropTypes.bool.isRequired,
|
|
|
uncompiled: PropTypes.bool,
|
|
uncompiled: PropTypes.bool,
|
|
|
|
|
+ validationIssues: PropTypes.object,
|
|
|
}),
|
|
}),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
export function CompileProvider({ children }) {
|
|
export function CompileProvider({ children }) {
|
|
|
|
|
+ const ide = useIdeContext()
|
|
|
|
|
+
|
|
|
|
|
+ const { hasPremiumCompile, isProjectOwner } = useEditorContext()
|
|
|
|
|
+
|
|
|
|
|
+ const project = useProjectContext()
|
|
|
|
|
+
|
|
|
|
|
+ const projectId = project._id
|
|
|
|
|
+
|
|
|
|
|
+ // whether a compile is in progress
|
|
|
|
|
+ const [compiling, setCompiling] = useState(false)
|
|
|
|
|
+
|
|
|
// the log entries parsed from the compile output log
|
|
// the log entries parsed from the compile output log
|
|
|
const [logEntries, setLogEntries] = useScopeValue('pdf.logEntries')
|
|
const [logEntries, setLogEntries] = useScopeValue('pdf.logEntries')
|
|
|
|
|
|
|
@@ -42,34 +87,368 @@ export function CompileProvider({ children }) {
|
|
|
// the id of the CLSI server which ran the compile
|
|
// the id of the CLSI server which ran the compile
|
|
|
const [clsiServerId, setClsiServerId] = useScopeValue('pdf.clsiServerId')
|
|
const [clsiServerId, setClsiServerId] = useScopeValue('pdf.clsiServerId')
|
|
|
|
|
|
|
|
|
|
+ // data received in response to a compile request
|
|
|
|
|
+ const [data, setData] = useState()
|
|
|
|
|
+
|
|
|
|
|
+ // whether the project has been compiled yet
|
|
|
|
|
+ const [compiledOnce, setCompiledOnce] = useState(false)
|
|
|
|
|
+
|
|
|
|
|
+ // whether the cache is being cleared
|
|
|
|
|
+ const [clearingCache, setClearingCache] = useState(false)
|
|
|
|
|
+
|
|
|
|
|
+ // whether the logs should be visible
|
|
|
|
|
+ const [showLogs, setShowLogs] = useState(false)
|
|
|
|
|
+
|
|
|
|
|
+ // an error that occurred
|
|
|
|
|
+ const [error, setError] = useState()
|
|
|
|
|
+
|
|
|
|
|
+ // the list of files that can be downloaded
|
|
|
|
|
+ const [fileList, setFileList] = useState()
|
|
|
|
|
+
|
|
|
|
|
+ // the raw contents of the log file
|
|
|
|
|
+ const [rawLog, setRawLog] = useState()
|
|
|
|
|
+
|
|
|
|
|
+ // validation issues from CLSI
|
|
|
|
|
+ const [validationIssues, setValidationIssues] = useState()
|
|
|
|
|
+
|
|
|
|
|
+ // whether autocompile is switched on
|
|
|
|
|
+ const [autoCompile, _setAutoCompile] = usePersistedState(
|
|
|
|
|
+ `autocompile_enabled:${projectId}`,
|
|
|
|
|
+ false,
|
|
|
|
|
+ true
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ // whether the compile should run in draft mode
|
|
|
|
|
+ const [draft, setDraft] = usePersistedState(`draft:${projectId}`, false, true)
|
|
|
|
|
+
|
|
|
|
|
+ // whether compiling should be prevented if there are linting errors
|
|
|
|
|
+ const [stopOnValidationError, setStopOnValidationError] = usePersistedState(
|
|
|
|
|
+ `stop_on_validation_error:${projectId}`,
|
|
|
|
|
+ true,
|
|
|
|
|
+ true
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ // the Document currently open in the editor
|
|
|
|
|
+ const [currentDoc] = useScopeValue('editor.sharejs_doc')
|
|
|
|
|
+
|
|
|
|
|
+ // whether the editor linter found errors
|
|
|
|
|
+ const [hasLintingError, setHasLintingError] = useScopeValue('hasLintingError')
|
|
|
|
|
+
|
|
|
|
|
+ // whether syntax validation is enabled globally
|
|
|
|
|
+ const [syntaxValidation] = useScopeValue('settings.syntaxValidation')
|
|
|
|
|
+
|
|
|
|
|
+ // the timestamp that a doc was last changed or saved
|
|
|
|
|
+ const [changedAt, setChangedAt] = useState(0)
|
|
|
|
|
+
|
|
|
|
|
+ const { signal } = useAbortController()
|
|
|
|
|
+
|
|
|
|
|
+ // the document compiler
|
|
|
|
|
+ const [compiler] = useState(() => {
|
|
|
|
|
+ return new DocumentCompiler({
|
|
|
|
|
+ project,
|
|
|
|
|
+ setChangedAt,
|
|
|
|
|
+ setCompiling,
|
|
|
|
|
+ setData,
|
|
|
|
|
+ setError,
|
|
|
|
|
+ signal,
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // keep currentDoc in sync with the compiler
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ compiler.currentDoc = currentDoc
|
|
|
|
|
+ }, [compiler, currentDoc])
|
|
|
|
|
+
|
|
|
|
|
+ // keep draft setting in sync with the compiler
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ compiler.draft = draft
|
|
|
|
|
+ }, [compiler, draft])
|
|
|
|
|
+
|
|
|
|
|
+ // pass the "uncompiled" value up into the scope for use outside this context provider
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ setUncompiled(changedAt > 0)
|
|
|
|
|
+ }, [setUncompiled, changedAt])
|
|
|
|
|
+
|
|
|
|
|
+ // record changes to the autocompile setting
|
|
|
|
|
+ const setAutoCompile = useCallback(
|
|
|
|
|
+ value => {
|
|
|
|
|
+ _setAutoCompile(value)
|
|
|
|
|
+ sendMB('autocompile-setting-changed', { value })
|
|
|
|
|
+ },
|
|
|
|
|
+ [_setAutoCompile]
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ // always compile the PDF once after opening the project, after the doc has loaded
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (!compiledOnce && currentDoc) {
|
|
|
|
|
+ setCompiledOnce(true)
|
|
|
|
|
+ compiler.compile({ isAutoCompileOnLoad: true })
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [compiledOnce, currentDoc, compiler])
|
|
|
|
|
+
|
|
|
|
|
+ // handle the data returned from a compile request
|
|
|
|
|
+ // note: this should _only_ run when `data` changes,
|
|
|
|
|
+ // the other dependencies must all be static
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (data) {
|
|
|
|
|
+ if (data.clsiServerId) {
|
|
|
|
|
+ setClsiServerId(data.clsiServerId) // set in scope, for PdfSynctexController
|
|
|
|
|
+ compiler.clsiServerId = data.clsiServerId
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (data.compileGroup) {
|
|
|
|
|
+ compiler.compileGroup = data.compileGroup
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (data.outputFiles) {
|
|
|
|
|
+ handleOutputFiles(projectId, data).then(result => {
|
|
|
|
|
+ setLogEntryAnnotations(
|
|
|
|
|
+ buildLogEntryAnnotations(result.logEntries.all, ide.fileTreeManager)
|
|
|
|
|
+ )
|
|
|
|
|
+ setLogEntries(result.logEntries)
|
|
|
|
|
+ setFileList(result.fileList)
|
|
|
|
|
+ setPdfDownloadUrl(result.pdfDownloadUrl)
|
|
|
|
|
+ setPdfUrl(result.pdfUrl)
|
|
|
|
|
+ setRawLog(result.log)
|
|
|
|
|
+
|
|
|
|
|
+ // sample compile stats for real users
|
|
|
|
|
+ if (!window.user.alphaProgram && data.status === 'success') {
|
|
|
|
|
+ sendMBSampled(
|
|
|
|
|
+ 'compile-result',
|
|
|
|
|
+ {
|
|
|
|
|
+ errors: result.logEntries.errors.length,
|
|
|
|
|
+ warnings: result.logEntries.warnings.length,
|
|
|
|
|
+ typesetting: result.logEntries.typesetting.length,
|
|
|
|
|
+ newPdfPreview: true, // TODO: is this useful?
|
|
|
|
|
+ },
|
|
|
|
|
+ 0.01
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch (data.status) {
|
|
|
|
|
+ case 'success':
|
|
|
|
|
+ setError(undefined)
|
|
|
|
|
+ setShowLogs(false)
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ case 'clsi-maintenance':
|
|
|
|
|
+ case 'compile-in-progress':
|
|
|
|
|
+ case 'exited':
|
|
|
|
|
+ case 'failure':
|
|
|
|
|
+ case 'project-too-large':
|
|
|
|
|
+ case 'rate-limited':
|
|
|
|
|
+ case 'terminated':
|
|
|
|
|
+ case 'too-recently-compiled':
|
|
|
|
|
+ setError(data.status)
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ case 'timedout':
|
|
|
|
|
+ setError('timedout')
|
|
|
|
|
+
|
|
|
|
|
+ if (!hasPremiumCompile && isProjectOwner) {
|
|
|
|
|
+ send(
|
|
|
|
|
+ 'subscription-funnel',
|
|
|
|
|
+ 'editor-click-feature',
|
|
|
|
|
+ 'compile-timeout'
|
|
|
|
|
+ )
|
|
|
|
|
+ sendMB('paywall-prompt', {
|
|
|
|
|
+ 'paywall-type': 'compile-timeout',
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ case 'autocompile-backoff':
|
|
|
|
|
+ if (!data.options.isAutoCompileOnLoad) {
|
|
|
|
|
+ setError('autocompile-disabled')
|
|
|
|
|
+ setAutoCompile(false)
|
|
|
|
|
+ sendMB('autocompile-rate-limited', { hasPremiumCompile })
|
|
|
|
|
+ }
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ case 'unavailable':
|
|
|
|
|
+ setError('clsi-unavailable')
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ case 'validation-problems':
|
|
|
|
|
+ setError('validation-problems')
|
|
|
|
|
+ setValidationIssues(data.validationProblems)
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
|
|
+ setError('error')
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [
|
|
|
|
|
+ compiler,
|
|
|
|
|
+ data,
|
|
|
|
|
+ ide,
|
|
|
|
|
+ hasPremiumCompile,
|
|
|
|
|
+ isProjectOwner,
|
|
|
|
|
+ projectId,
|
|
|
|
|
+ setAutoCompile,
|
|
|
|
|
+ setClsiServerId,
|
|
|
|
|
+ setLogEntries,
|
|
|
|
|
+ setLogEntryAnnotations,
|
|
|
|
|
+ setPdfDownloadUrl,
|
|
|
|
|
+ setPdfUrl,
|
|
|
|
|
+ ])
|
|
|
|
|
+
|
|
|
|
|
+ // switch to logs if there's an error
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (error) {
|
|
|
|
|
+ setShowLogs(true)
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [error])
|
|
|
|
|
+
|
|
|
|
|
+ // recompile on key press
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ const listener = event => {
|
|
|
|
|
+ compiler.compile(event.detail)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ window.addEventListener('pdf:recompile', listener)
|
|
|
|
|
+
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ window.removeEventListener('pdf:recompile', listener)
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [compiler])
|
|
|
|
|
+
|
|
|
|
|
+ // whether there has been an autocompile linting error, if syntax validation is switched on
|
|
|
|
|
+ const autoCompileLintingError = Boolean(
|
|
|
|
|
+ autoCompile && syntaxValidation && hasLintingError
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ const codeCheckFailed = stopOnValidationError && autoCompileLintingError
|
|
|
|
|
+
|
|
|
|
|
+ // show that the project has pending changes
|
|
|
|
|
+ const hasChanges = Boolean(
|
|
|
|
|
+ autoCompile && uncompiled && compiledOnce && !codeCheckFailed
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ // the project is available for auto-compiling
|
|
|
|
|
+ const canAutoCompile = Boolean(autoCompile && !compiling && !codeCheckFailed)
|
|
|
|
|
+
|
|
|
|
|
+ // call the debounced autocompile function if the project is available for auto-compiling and it has changed
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (canAutoCompile && changedAt > 0) {
|
|
|
|
|
+ compiler.debouncedAutoCompile()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ compiler.debouncedAutoCompile.cancel()
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [compiler, canAutoCompile, changedAt])
|
|
|
|
|
+
|
|
|
|
|
+ // cancel debounced recompile on unmount
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ compiler.debouncedAutoCompile.cancel()
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [compiler])
|
|
|
|
|
+
|
|
|
|
|
+ // record doc changes when notified by the editor
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ const listener = event => {
|
|
|
|
|
+ setChangedAt(Date.now())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ window.addEventListener('doc:changed', listener)
|
|
|
|
|
+ window.addEventListener('doc:saved', listener)
|
|
|
|
|
+
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ window.removeEventListener('doc:changed', listener)
|
|
|
|
|
+ window.removeEventListener('doc:saved', listener)
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [])
|
|
|
|
|
+
|
|
|
|
|
+ // start a compile manually
|
|
|
|
|
+ const startCompile = useCallback(() => {
|
|
|
|
|
+ compiler.compile()
|
|
|
|
|
+ }, [compiler])
|
|
|
|
|
+
|
|
|
|
|
+ // stop a compile manually
|
|
|
|
|
+ const stopCompile = useCallback(() => {
|
|
|
|
|
+ compiler.stopCompile()
|
|
|
|
|
+ }, [compiler])
|
|
|
|
|
+
|
|
|
|
|
+ // clear the compile cache
|
|
|
|
|
+ const clearCache = useCallback(() => {
|
|
|
|
|
+ setClearingCache(true)
|
|
|
|
|
+
|
|
|
|
|
+ return compiler.clearCache().finally(() => {
|
|
|
|
|
+ setClearingCache(false)
|
|
|
|
|
+ })
|
|
|
|
|
+ }, [compiler, setClearingCache])
|
|
|
|
|
+
|
|
|
|
|
+ // clear the cache then run a compile, triggered by a menu item
|
|
|
|
|
+ const recompileFromScratch = useCallback(() => {
|
|
|
|
|
+ clearCache().then(() => {
|
|
|
|
|
+ compiler.compile()
|
|
|
|
|
+ })
|
|
|
|
|
+ }, [clearCache, compiler])
|
|
|
|
|
+
|
|
|
const value = useMemo(
|
|
const value = useMemo(
|
|
|
() => ({
|
|
() => ({
|
|
|
|
|
+ autoCompile,
|
|
|
|
|
+ clearCache,
|
|
|
|
|
+ clearingCache,
|
|
|
clsiServerId,
|
|
clsiServerId,
|
|
|
|
|
+ codeCheckFailed,
|
|
|
|
|
+ compiling,
|
|
|
|
|
+ draft,
|
|
|
|
|
+ error,
|
|
|
|
|
+ fileList,
|
|
|
|
|
+ hasChanges,
|
|
|
|
|
+ hasLintingError,
|
|
|
logEntries,
|
|
logEntries,
|
|
|
logEntryAnnotations,
|
|
logEntryAnnotations,
|
|
|
pdfDownloadUrl,
|
|
pdfDownloadUrl,
|
|
|
pdfUrl,
|
|
pdfUrl,
|
|
|
- setClsiServerId,
|
|
|
|
|
- setLogEntries,
|
|
|
|
|
- setLogEntryAnnotations,
|
|
|
|
|
- setPdfDownloadUrl,
|
|
|
|
|
- setPdfUrl,
|
|
|
|
|
- setUncompiled,
|
|
|
|
|
|
|
+ rawLog,
|
|
|
|
|
+ recompileFromScratch,
|
|
|
|
|
+ setAutoCompile,
|
|
|
|
|
+ setClearingCache,
|
|
|
|
|
+ setCompiling,
|
|
|
|
|
+ setDraft,
|
|
|
|
|
+ setHasLintingError, // only for stories
|
|
|
|
|
+ setShowLogs,
|
|
|
|
|
+ setStopOnValidationError,
|
|
|
|
|
+ showLogs,
|
|
|
|
|
+ startCompile,
|
|
|
|
|
+ stopCompile,
|
|
|
|
|
+ stopOnValidationError,
|
|
|
uncompiled,
|
|
uncompiled,
|
|
|
|
|
+ validationIssues,
|
|
|
}),
|
|
}),
|
|
|
[
|
|
[
|
|
|
|
|
+ autoCompile,
|
|
|
|
|
+ clearCache,
|
|
|
|
|
+ clearingCache,
|
|
|
clsiServerId,
|
|
clsiServerId,
|
|
|
|
|
+ codeCheckFailed,
|
|
|
|
|
+ compiling,
|
|
|
|
|
+ draft,
|
|
|
|
|
+ error,
|
|
|
|
|
+ fileList,
|
|
|
|
|
+ hasChanges,
|
|
|
|
|
+ hasLintingError,
|
|
|
logEntries,
|
|
logEntries,
|
|
|
logEntryAnnotations,
|
|
logEntryAnnotations,
|
|
|
pdfDownloadUrl,
|
|
pdfDownloadUrl,
|
|
|
pdfUrl,
|
|
pdfUrl,
|
|
|
- setClsiServerId,
|
|
|
|
|
- setLogEntries,
|
|
|
|
|
- setLogEntryAnnotations,
|
|
|
|
|
- setPdfDownloadUrl,
|
|
|
|
|
- setPdfUrl,
|
|
|
|
|
- setUncompiled,
|
|
|
|
|
|
|
+ rawLog,
|
|
|
|
|
+ recompileFromScratch,
|
|
|
|
|
+ setAutoCompile,
|
|
|
|
|
+ setDraft,
|
|
|
|
|
+ setHasLintingError,
|
|
|
|
|
+ setStopOnValidationError,
|
|
|
|
|
+ showLogs,
|
|
|
|
|
+ startCompile,
|
|
|
|
|
+ stopCompile,
|
|
|
|
|
+ stopOnValidationError,
|
|
|
uncompiled,
|
|
uncompiled,
|
|
|
|
|
+ validationIssues,
|
|
|
]
|
|
]
|
|
|
)
|
|
)
|
|
|
|
|
|