diff --git a/Project.toml b/Project.toml index 5d58a281..0b636d7a 100644 --- a/Project.toml +++ b/Project.toml @@ -12,15 +12,20 @@ ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" -Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781" -PlutoStaticHTML = "359b1769-a58e-495b-9770-312e911026ad" +Requires = "ae029012-a4dd-5104-9daa-d747884805df" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" +[weakdeps] +PlutoStaticHTML = "359b1769-a58e-495b-9770-312e911026ad" + +[extensions] +PlutoNotebook = "PlutoStaticHTML" + [compat] Documenter = "0.22, 0.23, 0.24, 0.25, 0.26, 0.27" -Pluto = "^0.19.13" -PlutoStaticHTML = "^6" +PlutoStaticHTML = "6" +Requires = "1.3" FileIO = "1" HTTP = "0.6, 0.7, 0.8, 0.9, 1" ImageCore = "0.7, 0.8, 0.9" @@ -32,6 +37,7 @@ YAML = "0.3, 0.4" julia = "1.6" [extras] +PlutoStaticHTML = "359b1769-a58e-495b-9770-312e911026ad" ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1" ImageShow = "4e3cecfd-b093-5904-9786-8bbb286a6a31" ReferenceTests = "324d217c-45ce-50fc-942e-d289b448e8cf" @@ -39,4 +45,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990" [targets] -test = ["ImageMagick", "ImageShow", "ReferenceTests", "Test", "TestImages"] +test = ["ImageMagick", "ImageShow", "ReferenceTests", "Test", "TestImages", "PlutoStaticHTML"] diff --git a/ext/PlutoNotebook.jl b/ext/PlutoNotebook.jl new file mode 100644 index 00000000..6c2847d9 --- /dev/null +++ b/ext/PlutoNotebook.jl @@ -0,0 +1,127 @@ +module PlutoNotebook + +using Dates +using DemoCards: AbstractDemoCard, JULIA_COMPAT, load_config, download_badge, pluto_footer +import DemoCards: PlutoDemoCard, save_democards, make_badges, parse +isdefined(Base, :get_extension) ? (using PlutoStaticHTML) : (using ..PlutoStaticHTML) + + +function PlutoDemoCard(path::AbstractString)::PlutoDemoCard + # first consturct an incomplete democard, and then load the config + card = PlutoDemoCard(path, "", "", "", "", "", DateTime(0), JULIA_COMPAT, false) + + config = parse(card) + card.cover = load_config(card, "cover"; config = config) + card.title = load_config(card, "title"; config = config) + card.date = load_config(card, "date"; config = config) + card.author = load_config(card, "author"; config = config) + card.julia = load_config(card, "julia"; config = config) + # default id requires a title + card.id = load_config(card, "id"; config = config) + # default description requires a title + card.description = load_config(card, "description"; config = config) + card.hidden = load_config(card, "hidden"; config = config) + + return card +end + + +""" + save_democards(card_dir::AbstractString, card::PlutoDemoCard; + project_dir, + src, + credit, + nbviewer_root_url) + +process the original julia file and save it. + +The processing pipeline is: + +1. preprocess and copy source file +3. generate markdown file +4. insert header and footer to generated markdown file +""" +function save_democards( + card_dir::AbstractString, + card::PlutoDemoCard; + credit, + nbviewer_root_url, + project_dir = Base.source_dir(), + src = "src", + throw_error = false, + properties = Dict{String,Any}(), + kwargs..., +) + if !isabspath(card_dir) + card_dir = abspath(card_dir) + end + isdir(card_dir) || mkpath(card_dir) + @debug card.path + + # copy to card dir and do things + cardname = splitext(basename(card.path))[1] + # pluto outputs are expensive, we save the output to a cache dir + # these cache dir contains the render files from previous runs, + # saves time, while rendering + render_dir = joinpath(project_dir, "pluto_output") |> abspath + isdir(render_dir) || mkpath(render_dir) + + nb_path = joinpath(card_dir, "$(cardname).jl") + md_path = joinpath(card_dir, "$(cardname).md") + + cp(card.path, nb_path) + + if VERSION < card.julia + # It may work, it may not work; I hope it would work. + @warn "The running Julia version `$(VERSION)` is older than the declared compatible version `$(card.julia)`. You might need to upgrade your Julia." + end + + oopts = OutputOptions(; append_build_context = false) + output_format = documenter_output + bopts = BuildOptions(card_dir; previous_dir = render_dir, output_format = output_format) + # don't run notebooks in parallel + # TODO: User option to run it parallel or not + build_notebooks(bopts, ["$(cardname).jl"], oopts) + + # move rendered files to cache + cache_path = joinpath(render_dir, basename(md_path)) + cp(md_path, cache_path; force = true) + + badges = make_badges( + card; + src = src, + card_dir = card_dir, + nbviewer_root_url = nbviewer_root_url, + project_dir = project_dir, + build_notebook = false, + ) + + header = "# [$(card.title)](@id $(card.id))\n" + footer = pluto_footer + + body = join(readlines(md_path), "\n") + write(md_path, header, badges * "\n\n", body, footer) + + return nothing +end + +function make_badges( + card::PlutoDemoCard; + src, + card_dir, + nbviewer_root_url, + project_dir, + build_notebook, +) + cardname = splitext(basename(card.path))[1] + badges = [] + push!(badges, "[![Source code]($download_badge)]($(cardname).jl)") + + push!(badges, invoke(make_badges, Tuple{AbstractDemoCard}, card)) + + join(badges, " ") +end + +parse(card::PlutoDemoCard) = PlutoStaticHTML.Pluto.frontmatter(card.path) + +end diff --git a/src/DemoCards.jl b/src/DemoCards.jl index ba38ccdf..5a5328ce 100644 --- a/src/DemoCards.jl +++ b/src/DemoCards.jl @@ -6,8 +6,6 @@ using Mustache using Literate using ImageCore using FileIO, JSON, YAML -using Pluto -using PlutoStaticHTML using Suppressor # suppress log generated by 3rd party tools, e.g., Literate import HTTP using Documenter @@ -49,6 +47,15 @@ include("preview.jl") export makedemos, cardtheme, preview_demos +if !isdefined(Base, :get_extension) +using Requires +end + +function __init__() + @static if !isdefined(Base, :get_extension) + @require PlutoStaticHTML = "359b1769-a58e-495b-9770-312e911026ad" include("../ext/PlutoNotebook.jl") + end +end """ diff --git a/src/types/card.jl b/src/types/card.jl index ab9d1b35..04a5c3bf 100644 --- a/src/types/card.jl +++ b/src/types/card.jl @@ -24,7 +24,20 @@ function democard(path::String)::AbstractDemoCard return MarkdownDemoCard(path) elseif ext in julia_exts if is_pluto_notebook(path) - return PlutoDemoCard(path) + return try + PlutoDemoCard(path) + catch e + if isa(e, MethodError) + # method is not imported from PlutoNotebook + throw( + ErrorException( + "You need to load PlutoStaticHTML.jl for using pluto notebooks", + ), + ) + else + throw(e) + end + end else return JuliaDemoCard(path) end diff --git a/src/types/pluto.jl b/src/types/pluto.jl index dc91da4e..eccb27fa 100644 --- a/src/types/pluto.jl +++ b/src/types/pluto.jl @@ -60,118 +60,12 @@ See also: [`PlutoDemoCard`](@ref DemoCards.PlutoDemoCard), [`DemoSection`](@ref """ mutable struct PlutoDemoCard <: AbstractDemoCard path::String - cover::Union{String, Nothing} + cover::Union{String,Nothing} id::String title::String description::String author::String date::DateTime - julia::Union{Nothing, VersionNumber} + julia::Union{Nothing,VersionNumber} hidden::Bool end - -function PlutoDemoCard(path::AbstractString)::PlutoDemoCard - # first consturct an incomplete democard, and then load the config - card = PlutoDemoCard(path, "", "", "", "", "", DateTime(0), JULIA_COMPAT, false) - - config = parse(card) - card.cover = load_config(card, "cover"; config=config) - card.title = load_config(card, "title"; config=config) - card.date = load_config(card, "date"; config=config) - card.author = load_config(card, "author"; config=config) - card.julia = load_config(card, "julia"; config=config) - # default id requires a title - card.id = load_config(card, "id"; config=config) - # default description requires a title - card.description = load_config(card, "description"; config=config) - card.hidden = load_config(card, "hidden"; config=config) - - return card -end - - -""" - save_democards(card_dir::AbstractString, card::PlutoDemoCard; - project_dir, - src, - credit, - nbviewer_root_url) - -process the original julia file and save it. - -The processing pipeline is: - -1. preprocess and copy source file -3. generate markdown file -4. insert header and footer to generated markdown file -""" -function save_democards(card_dir::AbstractString, - card::PlutoDemoCard; - credit, - nbviewer_root_url, - project_dir=Base.source_dir(), - src="src", - throw_error = false, - properties = Dict{String, Any}(), - kwargs...) - if !isabspath(card_dir) - card_dir = abspath(card_dir) - end - isdir(card_dir) || mkpath(card_dir) - @debug card.path - - # copy to card dir and do things - cardname = splitext(basename(card.path))[1] - # pluto outputs are expensive, we save the output to a cache dir - # these cache dir contains the render files from previous runs, - # saves time, while rendering - render_dir = joinpath(project_dir, "pluto_output") |> abspath - isdir(render_dir) || mkpath(render_dir) - - nb_path = joinpath(card_dir, "$(cardname).jl") - md_path = joinpath(card_dir, "$(cardname).md") - - cp(card.path, nb_path) - - if VERSION < card.julia - # It may work, it may not work; I hope it would work. - @warn "The running Julia version `$(VERSION)` is older than the declared compatible version `$(card.julia)`. You might need to upgrade your Julia." - end - - oopts = OutputOptions(; append_build_context=false) - output_format = documenter_output - bopts = BuildOptions(card_dir;previous_dir=render_dir, - output_format=output_format) - # don't run notebooks in parallel - # TODO: User option to run it parallel or not - build_notebooks(bopts, ["$(cardname).jl"], oopts) - - # move rendered files to cache - cache_path = joinpath(render_dir, basename(md_path)) - cp(md_path, cache_path; force=true) - - badges = make_badges(card; - src=src, - card_dir=card_dir, - nbviewer_root_url=nbviewer_root_url, - project_dir=project_dir, - build_notebook=false) - - header = "# [$(card.title)](@id $(card.id))\n" - footer = pluto_footer - - body = join(readlines(md_path), "\n") - write(md_path, header, badges * "\n\n", body, footer) - - return nothing -end - -function make_badges(card::PlutoDemoCard; src, card_dir, nbviewer_root_url, project_dir, build_notebook) - cardname = splitext(basename(card.path))[1] - badges = [] - push!(badges, "[![Source code]($download_badge)]($(cardname).jl)") - - push!(badges, invoke(make_badges, Tuple{AbstractDemoCard}, card)) - - join(badges, " ") -end diff --git a/src/utils.jl b/src/utils.jl index ab5ac616..4d2f9581 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -189,14 +189,8 @@ Currently supported items are: `title`, `id`, `cover`, `description`. They also need to validate the values. """ function parse(T::Val, card::AbstractDemoCard) - # TODO: generalize - if T === Val(:Pluto) - frontmatter = [] - config = Pluto.frontmatter(card.path) - else - header, frontmatter, body = split_frontmatter(readlines(card.path)) - config = parse(T, body) - end + header, frontmatter, body = split_frontmatter(readlines(card.path)) + config = parse(T, body) # frontmatter has higher priority if !isempty(frontmatter) yaml_config = try @@ -213,7 +207,6 @@ function parse(T::Val, card::AbstractDemoCard) end parse(card::JuliaDemoCard) = parse(Val(:Julia), card) parse(card::MarkdownDemoCard) = parse(Val(:Markdown), card) -parse(card::PlutoDemoCard) = parse(Val(:Pluto), card) function parse(T::Val, contents::String)