Skip to content

Commit

Permalink
Support posit-dev/r-shinylive (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
schloerke authored Sep 12, 2023
1 parent 5dbe172 commit 1ce01dd
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 48 deletions.
2 changes: 1 addition & 1 deletion _extensions/quarto-ext/shinylive/_extension.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: shinylive
title: Embedded Shinylive applications
author: Winston Chang
version: 0.0.3
version: 0.0.4
quarto-required: ">=1.2.198"
contributes:
filters:
Expand Down
134 changes: 87 additions & 47 deletions _extensions/quarto-ext/shinylive/shinylive.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ local hasDoneShinyliveSetup = false
local codeblockScript = nil

-- Try calling `pandoc.pipe('shinylive', ...)` and if it fails, print a message
-- about installing shinylive package.
function callShinylive(args, input)
-- about installing shinylive python package.
function callPythonShinylive(args, input)
local res
local status, err = pcall(
function()
Expand All @@ -19,40 +19,72 @@ function callShinylive(args, input)
return res
end

-- Try calling `pandoc.pipe('Rscript', ...)` and if it fails, print a message
-- about installing shinylive R package.
function callRShinylive(args, input)
args = { "-e",
"shinylive::quarto_ext()",
table.unpack(args) }
local res
local status, err = pcall(
function()
res = pandoc.pipe("Rscript", args, input)
end
)

if not status then
print(err)
error(
"Error running 'Rscript' command. Perhaps you need to install the 'shinylive' R package?")
end

return res
end

function callShinylive(language, args, input)
-- print("Calling " .. language .. " shinylive with args: " .. table.concat(args, " "))
if language == "python" then
return callPythonShinylive(args, input)
elseif language == "r" then
return callRShinylive(args, input)
else
error("Unknown language: " .. language)
end
end

-- Do one-time setup when a Shinylive codeblock is encountered.
function ensureShinyliveSetup()
function ensureShinyliveSetup(language)
if hasDoneShinyliveSetup then
return
end
hasDoneShinyliveSetup = true

-- Find the path to codeblock-to-json.ts and save it for later use.
codeblockScript = callShinylive({ "codeblock-to-json-path" }, "")
codeblockScript = callShinylive(language, { "codeblock-to-json-path" }, "")
-- Remove trailing whitespace
codeblockScript = codeblockScript:gsub("%s+$", "")

local baseDeps = getShinyliveBaseDeps()
local baseDeps = getShinyliveBaseDeps(language)
for idx, dep in ipairs(baseDeps) do
quarto.doc.add_html_dependency(dep)
end

quarto.doc.add_html_dependency(
{
name = "shinylive-quarto-css",
stylesheets = {"resources/css/shinylive-quarto.css"}
stylesheets = { "resources/css/shinylive-quarto.css" }
}
)
end


function getShinyliveBaseDeps()
function getShinyliveBaseDeps(language)
-- Relative path from the current page to the root of the site. This is needed
-- to find out where shinylive-sw.js is, relative to the current page.
if quarto.project.offset == nil then
error("The shinylive extension must be used in a Quarto project directory (with a _quarto.yml file).")
end
local depJson = callShinylive(
language,
{ "base-deps", "--sw-dir", quarto.project.offset },
""
)
Expand All @@ -61,49 +93,57 @@ function getShinyliveBaseDeps()
return deps
end


return {
{
CodeBlock = function(el)
if el.attr and (
el.attr.classes:includes("{shinylive-python}")
or el.attr.classes:includes("{shinylive-r}")
) then
ensureShinyliveSetup()

-- Convert code block to JSON string in the same format as app.json.
local parsedCodeblockJson = pandoc.pipe(
"quarto",
{ "run", codeblockScript },
el.text
)

-- This contains "files" and "quartoArgs" keys.
local parsedCodeblock = quarto.json.decode(parsedCodeblockJson)

-- Find Python package dependencies for the current app.
local appDepsJson = callShinylive(
{ "package-deps" },
quarto.json.encode(parsedCodeblock["files"])
)

local appDeps = quarto.json.decode(appDepsJson)

for idx, dep in ipairs(appDeps) do
quarto.doc.attach_to_dependency("shinylive", dep)
end

if el.attr.classes:includes("{shinylive-python}") then
el.attributes.engine = "python"
el.attr.classes = pandoc.List()
el.attr.classes:insert("shinylive-python")
elseif el.attr.classes:includes("{shinylive-r}") then
el.attributes.engine = "r"
el.attr.classes = pandoc.List()
el.attr.classes:insert("shinylive-r")
end
return el
if not el.attr then
-- Not a shinylive codeblock, return
return
end

if el.attr.classes:includes("{shinylive-r}") then
language = "r"
elseif el.attr.classes:includes("{shinylive-python}") then
language = "python"
else
-- Not a shinylive codeblock, return
return
end
ensureShinyliveSetup(language)

-- Convert code block to JSON string in the same format as app.json.
local parsedCodeblockJson = pandoc.pipe(
"quarto",
{ "run", codeblockScript },
el.text
)

-- This contains "files" and "quartoArgs" keys.
local parsedCodeblock = quarto.json.decode(parsedCodeblockJson)

-- Find Python package dependencies for the current app.
local appDepsJson = callShinylive(
language,
{ "package-deps" },
quarto.json.encode(parsedCodeblock["files"])
)

local appDeps = quarto.json.decode(appDepsJson)

for idx, dep in ipairs(appDeps) do
quarto.doc.attach_to_dependency("shinylive", dep)
end

if el.attr.classes:includes("{shinylive-python}") then
el.attributes.engine = "python"
el.attr.classes = pandoc.List()
el.attr.classes:insert("shinylive-python")
elseif el.attr.classes:includes("{shinylive-r}") then
el.attributes.engine = "r"
el.attr.classes = pandoc.List()
el.attr.classes:insert("shinylive-r")
end
return el
end
}
}

0 comments on commit 1ce01dd

Please sign in to comment.