Serving

"""
Start a server to present built output of project `src` for the given
`targets`. The actions performed by each `target` are defined by the targets
themselves. The default target used is [`html`](#).
"""
function serve(src, targets...=html; port=8000, keywords...)
    @info "Serving '$src'..."
    old = pwd() # Save directory so it can be returned to in cleanup.
    p = Project(src; keywords...)
    dir = mktempdir()
    sw = LiveServer.SimpleWatcher()
    watch_files!(sw, p)
    callback = function (path) # Callback to update project and rerun
        @info "path: $path"    # given targets.
        revise(p)
        update!(p)
        @sync for f in targets
            @info "target: $f"
            @async f(p, dir)
        end
        watch_files!(sw, p)
    end
    LiveServer.set_callback!(sw, callback)
    LiveServer.start(sw)
    try
        # Run target-specific initialisation code.
        @sync for f in targets
            @async init(p, f; port=port, dir=dir)
        end
    finally
        # Cleanup steps.
        LiveServer.stop(sw)
        rm(dir; recursive=true, force=true)
        cd(old) # Reinstate original directory when server is terminated.
    end
end

"""
Set files to watch for changes within a given [`Project`](#) `p`.
"""
function watch_files!(sw::LiveServer.SimpleWatcher, p::Project)
    empty!(sw.watchedfiles) # TODO: proper API for this?
    for file in FileTrees.files(p.tree)
        f = string(path(file))
        LiveServer.is_watched(sw, f) || LiveServer.watch_file!(sw, f)
    end
end

"""
Function for defining the initialisation code called by [`serve`](#)
for a specific target output.

Should be defined for each valid output target, i.e [`html`](#), [`pdf`](#),
etc. Signature should follow the following template:

```julia
function init(project::Project, ::typeof(target); kws...)

end
```

where `target` is the `Function` defining the output format.
"""
function init end