Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support posit-dev/r-shinylive #21

Merged
merged 4 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}