Skip to content

Commit 4620f1b

Browse files
Add MultipleFiles as possible generator output
1 parent f2ce6bd commit 4620f1b

File tree

2 files changed

+117
-50
lines changed

2 files changed

+117
-50
lines changed

src/Fornax.Core/Model.fs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,7 +1273,7 @@ open System.Collections.Generic
12731273

12741274
type SiteErrors = string list
12751275

1276-
type GenerationPhase =
1276+
type GenerationPhase =
12771277
| Loading
12781278
| Generating
12791279
type SiteError = {
@@ -1295,7 +1295,7 @@ type SiteContents () =
12951295
| _ -> None
12961296

12971297
member __.Errors () =
1298-
List.ofSeq errors.Values
1298+
List.ofSeq errors.Values
12991299

13001300
member __.Add(value:'a) =
13011301
let key = typeof<List<'a>>
@@ -1337,6 +1337,7 @@ module Config =
13371337
| ChangeExtension of newExtension: string
13381338
| NewFileName of newFileName: string
13391339
| Custom of (string -> string)
1340+
| MultipleFiles of (string -> string)
13401341

13411342
type GeneratorConfig = {
13421343
Script: string

src/Fornax/Generator.fs

Lines changed: 114 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ module EvaluatorHelpers =
4848
path.Replace("\\", "\\\\")
4949

5050
let internal invokeFunction (f : obj) (args : obj seq) =
51-
// Recusive partial evaluation of f, terminate when no args are left.
51+
// Recusive partial evaluation of f, terminate when no args are left.
5252
let rec helper (next : obj) (args : obj list) =
5353
match args with
5454
| head::tail ->
@@ -62,7 +62,7 @@ module EvaluatorHelpers =
6262
helper res tail
6363
else None // Error case, arg exists but can't be applied
6464
| [] ->
65-
Some next
65+
Some next
6666
helper f (args |> List.ofSeq )
6767

6868
let internal compileExpression (input : FsiValue) =
@@ -115,9 +115,9 @@ module LoaderEvaluator =
115115
runLoader fsi loaderPath
116116
|> Result.bind (fun ft ->
117117
let generator = compileExpression ft
118-
invokeFunction generator [box projectRoot; box siteContent]
118+
invokeFunction generator [box projectRoot; box siteContent]
119119
|> function
120-
| Some r ->
120+
| Some r ->
121121
try r :?> SiteContents |> Ok
122122
with _ -> sprintf "File loader %s incorrect return type" loaderPath |> Error
123123
| None -> sprintf "File loader %s couldn't be compiled" loaderPath |> Error)
@@ -171,12 +171,26 @@ module GeneratorEvaluator =
171171
|> Result.bind (fun ft ->
172172
let generator = compileExpression ft
173173

174-
invokeFunction generator [box siteContent; box projectRoot; box page ]
174+
invokeFunction generator [box siteContent; box projectRoot; box page ]
175175
|> Option.bind (tryUnbox<string>)
176176
|> function
177177
| Some s -> Ok s
178178
| None -> sprintf "HTML generator %s couldn't be compiled" generatorPath |> Error)
179179

180+
///`generatorPath` - absolute path to `.fsx` file containing the generator
181+
///`projectRoot` - path to root of the site project
182+
///`page` - path to the file that should be transformed
183+
let evaluateMultiple (fsi : FsiEvaluationSession) (siteContent : SiteContents) (generatorPath : string) (projectRoot: string) (page: string) =
184+
getGeneratorContent fsi generatorPath
185+
|> Result.bind (fun ft ->
186+
let generator = compileExpression ft
187+
188+
invokeFunction generator [box siteContent; box projectRoot; box page ]
189+
|> Option.bind (tryUnbox<(string * string) list>)
190+
|> function
191+
| Some s -> Ok s
192+
| None -> sprintf "HTML generator %s couldn't be compiled" generatorPath |> Error)
193+
180194
module ConfigEvaluator =
181195
open FSharp.Compiler.Interactive.Shell
182196
open EvaluatorHelpers
@@ -241,11 +255,15 @@ type GeneratorResult =
241255
| GeneratorSuccess of GeneratorMessage option
242256
| GeneratorFailure of GeneratorMessage
243257

258+
type GeneratorPick =
259+
| Simple of genartorPath : string * outputPath: string
260+
| Multiple of generatorPath: string * outputMapper: (string -> string)
261+
244262
let pickGenerator (cfg: Config.Config) (siteContent : SiteContents) (projectRoot : string) (page: string) =
245-
let generator =
263+
let generator =
246264
match siteContent.TryGetError page with
247265
| Some _ -> None
248-
| None ->
266+
| None ->
249267
cfg.Generators |> List.tryFind (fun n ->
250268
match n.Trigger with
251269
| Once -> false //Once-trigger run globally, not for particular file
@@ -256,16 +274,21 @@ let pickGenerator (cfg: Config.Config) (siteContent : SiteContents) (projectRoo
256274
match generator with
257275
| None -> None
258276
| Some generator ->
259-
let outputPath =
260-
let newPage =
261-
match generator.OutputFile with
262-
| SameFileName -> page
263-
| ChangeExtension(newExtension) -> Path.ChangeExtension(page, newExtension)
264-
| NewFileName(newFileName) -> newFileName
265-
| Custom(handler) -> handler page
266-
Path.Combine(projectRoot, "_public", newPage)
267277
let generatorPath = Path.Combine(projectRoot, "generators", generator.Script)
268-
Some(generatorPath, outputPath)
278+
match generator.OutputFile with
279+
| MultipleFiles mapper ->
280+
Some(Multiple (generatorPath, mapper))
281+
| _ ->
282+
let outputPath =
283+
let newPage =
284+
match generator.OutputFile with
285+
| SameFileName -> page
286+
| ChangeExtension(newExtension) -> Path.ChangeExtension(page, newExtension)
287+
| NewFileName(newFileName) -> newFileName
288+
| Custom(handler) -> handler page
289+
| MultipleFiles(_) -> failwith "Shouldn't happen"
290+
Path.Combine(projectRoot, "_public", newPage)
291+
Some(Simple (generatorPath, outputPath))
269292

270293

271294
///`projectRoot` - path to the root of website
@@ -275,39 +298,9 @@ let generate fsi (cfg: Config.Config) (siteContent : SiteContents) (projectRoot
275298
match pickGenerator cfg siteContent projectRoot page with
276299
| None ->
277300
GeneratorIgnored
278-
| Some (layoutPath, outputPath) ->
279-
280-
let result = GeneratorEvaluator.evaluate fsi siteContent layoutPath projectRoot page
281-
match result with
282-
| Ok r ->
283-
let dir = Path.GetDirectoryName outputPath
284-
if not (Directory.Exists dir) then Directory.CreateDirectory dir |> ignore
285-
File.WriteAllText(outputPath, r)
286-
let endTime = DateTime.Now
287-
let ms = (endTime - startTime).Milliseconds
288-
sprintf "[%s] '%s' generated in %dms" (endTime.ToString("HH:mm:ss")) outputPath ms
289-
|> Some
290-
|> GeneratorSuccess
291-
| Error message ->
292-
let endTime = DateTime.Now
293-
sprintf "[%s] '%s' generation failed" (endTime.ToString("HH:mm:ss")) outputPath
294-
|> (fun s -> message + Environment.NewLine + s)
295-
|> GeneratorFailure
301+
| Some (Simple(layoutPath, outputPath)) ->
296302

297-
let runOnceGenerators fsi (cfg: Config.Config) (siteContent : SiteContents) (projectRoot : string) =
298-
cfg.Generators
299-
|> List.filter (fun n -> match n.Trigger with | Once -> true | _ -> false)
300-
|> List.filter (fun n -> match n.OutputFile with | NewFileName _ -> true | _ -> false)
301-
|> List.map (fun generator ->
302-
let startTime = DateTime.Now
303-
let outputPath =
304-
let newPage =
305-
match generator.OutputFile with
306-
| NewFileName(newFileName) -> newFileName
307-
| _ -> failwith "Shouldn't happen"
308-
Path.Combine(projectRoot, "_public", newPage)
309-
let generatorPath = Path.Combine(projectRoot, "generators", generator.Script)
310-
let result = GeneratorEvaluator.evaluate fsi siteContent generatorPath projectRoot ""
303+
let result = GeneratorEvaluator.evaluate fsi siteContent layoutPath projectRoot page
311304
match result with
312305
| Ok r ->
313306
let dir = Path.GetDirectoryName outputPath
@@ -323,6 +316,79 @@ let runOnceGenerators fsi (cfg: Config.Config) (siteContent : SiteContents) (pro
323316
sprintf "[%s] '%s' generation failed" (endTime.ToString("HH:mm:ss")) outputPath
324317
|> (fun s -> message + Environment.NewLine + s)
325318
|> GeneratorFailure
319+
| Some (Multiple(layoutPath, mapper)) ->
320+
let result = GeneratorEvaluator.evaluateMultiple fsi siteContent layoutPath projectRoot page
321+
match result with
322+
| Ok results ->
323+
results |>
324+
List.iter (fun (o, r) ->
325+
let outputPath = mapper o
326+
let outputPath = Path.Combine(projectRoot, "_public", outputPath)
327+
let dir = Path.GetDirectoryName outputPath
328+
if not (Directory.Exists dir) then Directory.CreateDirectory dir |> ignore
329+
File.WriteAllText(outputPath, r)
330+
)
331+
let endTime = DateTime.Now
332+
let ms = (endTime - startTime).Milliseconds
333+
sprintf "[%s] multiple files generated in %dms" (endTime.ToString("HH:mm:ss")) ms
334+
|> Some
335+
|> GeneratorSuccess
336+
| Error message ->
337+
let endTime = DateTime.Now
338+
sprintf "[%s] multiple files generation failed" (endTime.ToString("HH:mm:ss"))
339+
|> (fun s -> message + Environment.NewLine + s)
340+
|> GeneratorFailure
341+
342+
let runOnceGenerators fsi (cfg: Config.Config) (siteContent : SiteContents) (projectRoot : string) =
343+
cfg.Generators
344+
|> List.filter (fun n -> match n.Trigger with | Once -> true | _ -> false)
345+
|> List.filter (fun n -> match n.OutputFile with | NewFileName _ | MultipleFiles _ -> true | _ -> false)
346+
|> List.map (fun generator ->
347+
let startTime = DateTime.Now
348+
let generatorPath = Path.Combine(projectRoot, "generators", generator.Script)
349+
match generator.OutputFile with
350+
| NewFileName newFileName ->
351+
352+
let outputPath = Path.Combine(projectRoot, "_public", newFileName)
353+
let result = GeneratorEvaluator.evaluate fsi siteContent generatorPath projectRoot ""
354+
match result with
355+
| Ok r ->
356+
let dir = Path.GetDirectoryName outputPath
357+
if not (Directory.Exists dir) then Directory.CreateDirectory dir |> ignore
358+
File.WriteAllText(outputPath, r)
359+
let endTime = DateTime.Now
360+
let ms = (endTime - startTime).Milliseconds
361+
sprintf "[%s] '%s' generated in %dms" (endTime.ToString("HH:mm:ss")) outputPath ms
362+
|> Some
363+
|> GeneratorSuccess
364+
| Error message ->
365+
let endTime = DateTime.Now
366+
sprintf "[%s] '%s' generation failed" (endTime.ToString("HH:mm:ss")) outputPath
367+
|> (fun s -> message + Environment.NewLine + s)
368+
|> GeneratorFailure
369+
| MultipleFiles mapper ->
370+
let result = GeneratorEvaluator.evaluateMultiple fsi siteContent generatorPath projectRoot ""
371+
match result with
372+
| Ok results ->
373+
results |>
374+
List.iter (fun (o, r) ->
375+
let outputPath = mapper o
376+
let outputPath = Path.Combine(projectRoot, "_public", outputPath)
377+
let dir = Path.GetDirectoryName outputPath
378+
if not (Directory.Exists dir) then Directory.CreateDirectory dir |> ignore
379+
File.WriteAllText(outputPath, r)
380+
)
381+
let endTime = DateTime.Now
382+
let ms = (endTime - startTime).Milliseconds
383+
sprintf "[%s] multiple files generated in %dms" (endTime.ToString("HH:mm:ss")) ms
384+
|> Some
385+
|> GeneratorSuccess
386+
| Error message ->
387+
let endTime = DateTime.Now
388+
sprintf "[%s] multiple files generation failed" (endTime.ToString("HH:mm:ss"))
389+
|> (fun s -> message + Environment.NewLine + s)
390+
|> GeneratorFailure
391+
| _ -> failwith "Shouldn't happen"
326392
)
327393

328394
// Module to print colored message in the console

0 commit comments

Comments
 (0)