Compare commits
2 Commits
8abe1d1bc2
...
6b49db5801
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b49db5801 | |||
| b0457388dc |
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,6 +1,8 @@
|
||||
dist-newstyle
|
||||
dist-newstyle
|
||||
/.stack-work/
|
||||
/.stack-root/
|
||||
/.cabal-config/
|
||||
*.mmd
|
||||
*.png
|
||||
*.svg
|
||||
@ -10,3 +12,10 @@ dist-newstyle
|
||||
dist-newstyle
|
||||
output/
|
||||
*.log
|
||||
cabal.project
|
||||
lts-24-34.yaml
|
||||
stack-setup-2.yaml
|
||||
analytics-charts.md
|
||||
architecture-deep-dive.md
|
||||
devcontainer.org
|
||||
root.json
|
||||
|
||||
@ -47,7 +47,6 @@ data CompilationContext = CompilationContext
|
||||
, ccDocName :: Text
|
||||
, ccReaderOptions :: ReaderOptions
|
||||
, ccConfig :: DiagramConfig
|
||||
, ccWritesFile :: Bool
|
||||
}
|
||||
|
||||
-- | Monad stack for compilation pipeline
|
||||
@ -57,44 +56,57 @@ type CompilationM = ReaderT CompilationContext (ExceptT DocsterError IO)
|
||||
data CompilationStrategy = CompilationStrategy
|
||||
{ -- | Format for diagram configuration
|
||||
csOutputFormat :: OutputFormat
|
||||
-- | Pandoc writer function (returns Text for HTML/PDF, unused for DOCX)
|
||||
, csWriter :: WriterOptions -> Pandoc -> PandocIO Text
|
||||
-- | Post-processing function for the generated content
|
||||
, csProcessOutput :: String -> Text -> IO (Either DocsterError ())
|
||||
-- | Pandoc writer: writes output directly to the given file path
|
||||
, csWriter :: WriterOptions -> Pandoc -> FilePath -> IO (Either DocsterError ())
|
||||
-- | Post-processing after write (PDF→xelatex, HTML→open browser, DOCX→noop)
|
||||
, csPostProcess :: String -> IO (Either DocsterError ())
|
||||
-- | Success message formatter
|
||||
, csSuccessMessage :: String -> Text
|
||||
-- | True for formats where writer writes a file directly (DOCX)
|
||||
, csWritesFile :: Bool
|
||||
}
|
||||
|
||||
-- | PDF compilation strategy
|
||||
pdfStrategy :: CompilationStrategy
|
||||
pdfStrategy = CompilationStrategy
|
||||
{ csOutputFormat = PDF
|
||||
, csWriter = writeLaTeX
|
||||
, csProcessOutput = processPDFOutput
|
||||
, csWriter = \opts doc path -> do
|
||||
result <- runIO (writeLaTeX opts doc)
|
||||
case result of
|
||||
Left err -> return $ Left $ FileError $ "LaTeX write failed: " <> T.pack (show err)
|
||||
Right latex -> do
|
||||
TIO.writeFile path (latexTemplate latex)
|
||||
return $ Right ()
|
||||
, csPostProcess = processPDFOutput
|
||||
, csSuccessMessage = \path -> successEmoji <> " PDF written to " <> T.pack path
|
||||
, csWritesFile = False
|
||||
}
|
||||
|
||||
-- | HTML compilation strategy
|
||||
htmlStrategy :: CompilationStrategy
|
||||
htmlStrategy = CompilationStrategy
|
||||
{ csOutputFormat = HTML
|
||||
, csWriter = writeHtml5String
|
||||
, csProcessOutput = processHTMLOutput
|
||||
, csWriter = \opts doc path -> do
|
||||
result <- runIO (writeHtml5String opts doc)
|
||||
case result of
|
||||
Left err -> return $ Left $ FileError $ "HTML write failed: " <> T.pack (show err)
|
||||
Right html -> do
|
||||
TIO.writeFile path html
|
||||
return $ Right ()
|
||||
, csPostProcess = processHTMLOutput
|
||||
, csSuccessMessage = \path -> successEmoji <> " HTML written to " <> T.pack path
|
||||
, csWritesFile = False
|
||||
}
|
||||
|
||||
-- | DOCX compilation strategy (Pandoc writes file directly)
|
||||
docxStrategy :: CompilationStrategy
|
||||
docxStrategy = CompilationStrategy
|
||||
{ csOutputFormat = DOCX
|
||||
, csWriter = \_ _ -> return "" -- unused: writeDocx writes file directly
|
||||
, csProcessOutput = \_ _ -> return $ Right () -- no post-processing needed
|
||||
, csWriter = \opts doc path -> do
|
||||
result <- runIO (writeDocx opts doc)
|
||||
case result of
|
||||
Left err -> return $ Left $ FileError $ "DOCX generation failed: " <> T.pack (show err)
|
||||
Right docxBS -> do
|
||||
BSL.writeFile path docxBS
|
||||
return $ Right ()
|
||||
, csPostProcess = \_ -> return $ Right () -- no post-processing needed
|
||||
, csSuccessMessage = \path -> successEmoji <> " DOCX written to " <> T.pack path
|
||||
, csWritesFile = True
|
||||
}
|
||||
|
||||
-- | Parse LaTeX log content to extract meaningful error messages
|
||||
@ -160,11 +172,10 @@ extractFatalErrors = mapMaybe extractFatal
|
||||
| "! " `T.isPrefixOf` line && not ("Missing character:" `T.isInfixOf` line) = Just $ T.drop 2 line
|
||||
| otherwise = Nothing
|
||||
|
||||
-- | Process PDF output: LaTeX template application and direct XeLaTeX compilation
|
||||
processPDFOutput :: String -> Text -> IO (Either DocsterError ())
|
||||
processPDFOutput outputPath latexOutput = do
|
||||
let completeLatex = latexTemplate latexOutput
|
||||
logOutputPath = replaceExtension outputPath "log"
|
||||
-- | Process PDF output: direct XeLaTeX compilation (LaTeX already written by csWriter)
|
||||
processPDFOutput :: String -> IO (Either DocsterError ())
|
||||
processPDFOutput outputPath = do
|
||||
let logOutputPath = replaceExtension outputPath "log"
|
||||
|
||||
-- Use temporary directory for LaTeX compilation
|
||||
withSystemTempDirectory "docster-latex" $ \tempDir -> do
|
||||
@ -172,9 +183,6 @@ processPDFOutput outputPath latexOutput = do
|
||||
pdfFile = tempDir </> "document.pdf"
|
||||
logFile = tempDir </> "document.log"
|
||||
|
||||
-- Write LaTeX content to temporary file
|
||||
TIO.writeFile texFile completeLatex
|
||||
|
||||
-- Run XeLaTeX compilation
|
||||
(exitCode, _stdout, stderr) <- readProcessWithExitCode "xelatex"
|
||||
[ "-output-directory=" <> tempDir
|
||||
@ -210,11 +218,9 @@ processPDFOutput outputPath latexOutput = do
|
||||
errorSummary <> "\n\n" <>
|
||||
"Full LaTeX log written to: " <> T.pack logOutputPath
|
||||
|
||||
-- | Process HTML output: file writing and browser opening
|
||||
processHTMLOutput :: String -> Text -> IO (Either DocsterError ())
|
||||
processHTMLOutput outputPath html = do
|
||||
TIO.writeFile outputPath html
|
||||
|
||||
-- | Process HTML output: open browser (HTML already written by csWriter)
|
||||
processHTMLOutput :: String -> IO (Either DocsterError ())
|
||||
processHTMLOutput outputPath = do
|
||||
-- Open the generated HTML file in browser for verification
|
||||
putStrLn $ "🌐 Opening " <> outputPath <> " in browser for error checking..."
|
||||
void $ callProcess "open" [outputPath]
|
||||
@ -267,28 +273,13 @@ transformDocumentM pandoc = do
|
||||
docName <- asks ccDocName
|
||||
liftEitherM $ transformDocument config docName pandoc
|
||||
|
||||
-- | Pipeline step: Generate output using format-specific writer
|
||||
generateOutputM :: Pandoc -> CompilationM Text
|
||||
generateOutputM pandoc = do
|
||||
-- | Pipeline step: Write output and post-process (format-specific)
|
||||
writeAndProcessOutput :: Pandoc -> CompilationM ()
|
||||
writeAndProcessOutput pandoc = do
|
||||
strategy <- asks ccStrategy
|
||||
writesFile <- asks ccWritesFile
|
||||
if writesFile
|
||||
then do
|
||||
outputPath <- asks ccOutputPath
|
||||
_ <- liftIO $ generateOutputFile strategy outputPath pandoc
|
||||
return "" -- placeholder, won't be used
|
||||
else liftEitherM $ generateOutput strategy pandoc
|
||||
|
||||
-- | Pipeline step: Process output and write to file
|
||||
processOutput :: Text -> CompilationM ()
|
||||
processOutput output = do
|
||||
strategy <- asks ccStrategy
|
||||
writesFile <- asks ccWritesFile
|
||||
if writesFile
|
||||
then return () -- file already written by writer
|
||||
else do
|
||||
outputPath <- asks ccOutputPath
|
||||
liftEitherM $ csProcessOutput strategy outputPath output
|
||||
liftEitherM $ csWriter strategy def pandoc outputPath
|
||||
liftEitherM $ (csPostProcess strategy) outputPath
|
||||
|
||||
-- | Pipeline step: Print success message
|
||||
printSuccess :: CompilationM ()
|
||||
@ -302,8 +293,8 @@ compileWithStrategy :: CompilationStrategy -> SourceDir -> OutputDir -> Text ->
|
||||
compileWithStrategy strategy sourceDir outputDir docName (OutputPath inputPath) (OutputPath outputPath) = do
|
||||
let readerOptions = def { readerExtensions = getDefaultExtensions "markdown" }
|
||||
config = DiagramConfig sourceDir outputDir (csOutputFormat strategy)
|
||||
context = CompilationContext strategy inputPath outputPath docName readerOptions config (csWritesFile strategy)
|
||||
pipeline = readContent >>= parseDocument >>= transformDocumentM >>= generateOutputM >>= processOutput >> printSuccess
|
||||
context = CompilationContext strategy inputPath outputPath docName readerOptions config
|
||||
pipeline = readContent >>= parseDocument >>= transformDocumentM >>= writeAndProcessOutput >> printSuccess
|
||||
|
||||
runExceptT $ runReaderT pipeline context
|
||||
|
||||
@ -315,26 +306,7 @@ parseMarkdown readerOptions content = do
|
||||
Left err -> Left $ FileError $ "Failed to parse markdown: " <> T.pack (show err)
|
||||
Right pandoc -> Right pandoc
|
||||
|
||||
-- | Generate output using the strategy's writer with error handling
|
||||
generateOutput :: CompilationStrategy -> Pandoc -> IO (Either DocsterError Text)
|
||||
generateOutput strategy transformed = do
|
||||
result <- runIO $ csWriter strategy def transformed
|
||||
return $ case result of
|
||||
Left err -> Left $ case csOutputFormat strategy of
|
||||
PDF -> PDFGenerationError $ "LaTeX generation failed: " <> T.pack (show err)
|
||||
HTML -> FileError $ "HTML generation failed: " <> T.pack (show err)
|
||||
DOCX -> FileError $ "DOCX generation failed: " <> T.pack (show err)
|
||||
Right output -> Right output
|
||||
|
||||
-- | Generate output file directly (for DOCX which writes to file)
|
||||
generateOutputFile :: CompilationStrategy -> FilePath -> Pandoc -> IO (Either DocsterError ())
|
||||
generateOutputFile _ outputPath pandoc = do
|
||||
result <- runIO $ writeDocx def pandoc
|
||||
case result of
|
||||
Left err -> return $ Left $ FileError $ "DOCX generation failed: " <> T.pack (show err)
|
||||
Right docxBS -> do
|
||||
BSL.writeFile outputPath docxBS
|
||||
return $ Right ()
|
||||
|
||||
-- | Compile markdown to PDF using XeLaTeX
|
||||
compileToPDF :: FilePath -> IO ()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user