Skip to content

Commit 7507efe

Browse files
authored
Merge pull request #16 from 360-info/dev
v0.0.2
2 parents 35b46f3 + c5b7c1a commit 7507efe

File tree

8 files changed

+373
-13
lines changed

8 files changed

+373
-13
lines changed

.quartoignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
README.md
2+
NEWS.md
23
LICENSE
4+
docs/
5+
.quarto/
6+
!.gitignore

NEWS.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
## Sverto 0.0.2
2+
3+
- Bump minimum Quarto version to 1.3.0.
4+
- Fixes for compatibility with newer Quarto 1.3 pre-releases
5+
- Quarto's switch from Pandoc 2 to Pandoc 3 caused some issues with the way Sverto identifies Svelte import statements. This should no longer be a problem.
6+
- We now take advantage of the improved `.quartoignore` functionality in Quarto 1.3 to:
7+
1. avoid copying the `docs` folder in with the project template; and
8+
2. include the `.gitignore` with the template
9+
10+
## 0.0.1
11+
12+
- Initial release

_extensions/sverto/_extension.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
title: Sverto
22
author: 360info
3-
version: 0.0.1
4-
quarto-version: ">=1.2.0"
3+
version: 0.0.2
4+
quarto-version: ">=1.3.0"
55
contributes:
66
project:
77
project:

_extensions/sverto/create-imports.lua

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
-- create-imports: a project pre-render script that:
2+
-- * replaces svelte_import() ojs statements in qmd files, saving to /.sverto
3+
-- * writes svelte import paths to /.sverto s othey can be compiled
4+
5+
-- some content from quarto's qmd-reader.lua
6+
-- (c) 2023 rstudio, pbc. licensed under gnu gpl v2:
7+
-- https://github.com/quarto-dev/quarto-cli/blob/main/COPYRIGHT
8+
19
-- return contents of named file
210
function read_file(name)
311
local file = io.open(name, "r")
@@ -53,10 +61,114 @@ function path_dir(path)
5361
return path:match("(.*".. get_path_sep() ..")") or ""
5462
end
5563

56-
-- TODO -
64+
-- content following from quarto's qmd-reader.lua
65+
-- (c) 2023 rstudio, pbc. licensed under gnu gpl v2:
66+
-- https://github.com/quarto-dev/quarto-cli/blob/main/COPYRIGHT
67+
68+
function random_string(size)
69+
-- we replace invalid tags with random strings of the same size
70+
-- to safely allow code blocks inside pipe tables
71+
-- note that we can't use uppercase letters here
72+
-- because pandoc canonicalizes classes to lowercase.
73+
local chars = "abcdefghijklmnopqrstuvwxyz"
74+
local lst = {}
75+
for _ = 1,size do
76+
local ix = math.random(1, #chars)
77+
table.insert(lst, string.sub(chars, ix, ix))
78+
end
79+
return table.concat(lst, "")
80+
end
81+
82+
function find_invalid_tags(str)
83+
-- [^.=\n]
84+
-- we disallow "." to avoid catching {.python}
85+
-- we disallow "=" to avoid catching {foo="bar"}
86+
-- we disallow "\n" to avoid multiple lines
87+
88+
-- no | in lua patterns...
89+
90+
-- (c standard, 7.4.1.10, isspace function)
91+
-- %s catches \n and \r, so we must use [ \t\f\v] instead
92+
93+
local patterns = {
94+
"^[ \t\f\v]*(```+[ \t\f\v]*)(%{+[^.=\n\r]*%}+)",
95+
"\n[ \t\f\v]*(```+[ \t\f\v]*)(%{+[^.=\n\r]+%}+)"
96+
}
97+
local function find_it(init)
98+
for _, pattern in ipairs(patterns) do
99+
local range_start, range_end, ticks, tag = str:find(pattern, init)
100+
if range_start ~= nil then
101+
return range_start, range_end, ticks, tag
102+
end
103+
end
104+
return nil
105+
end
106+
107+
local init = 1
108+
local range_start, range_end, ticks, tag = find_it(init)
109+
local tag_set = {}
110+
local tags = {}
111+
while tag ~= nil do
112+
init = range_end + 1
113+
if not tag_set[tag] then
114+
tag_set[tag] = true
115+
table.insert(tags, tag)
116+
end
117+
range_start, range_end, ticks, tag = find_it(init)
118+
end
119+
return tags
120+
end
121+
122+
function escape_invalid_tags(str)
123+
local tags = find_invalid_tags(str)
124+
-- we must now replace the tags in a careful order. Specifically,
125+
-- we can't replace a key that's a substring of a larger key without
126+
-- first replacing the larger key.
127+
--
128+
-- ie. if we replace {python} before {{python}}, Bad Things Happen.
129+
-- so we sort the tags by descending size, which suffices
130+
table.sort(tags, function(a, b) return #b < #a end)
131+
132+
local replacements = {}
133+
for _, k in ipairs(tags) do
134+
local replacement
135+
local attempts = 1
136+
repeat
137+
replacement = random_string(#k)
138+
attempts = attempts + 1
139+
until str:find(replacement, 1, true) == nil or attempts == 100
140+
if attempts == 100 then
141+
print("Internal error, could not find safe replacement for "..k.." after 100 tries")
142+
print("Please file a bug at https://github.com/quarto-dev/quarto-cli")
143+
os.exit(1)
144+
end
145+
-- replace all lua special pattern characters with their
146+
-- escaped versions
147+
local safe_pattern = k:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
148+
replacements[replacement] = k
149+
local patterns = {
150+
"^([ \t\f\v]*```+[ \t\f\v]*)" .. safe_pattern,
151+
"(\n[ \t\f\v]*```+[ \t\f\v]*)" .. safe_pattern
152+
}
153+
154+
str = str:gsub(patterns[1], "%1" .. replacement):gsub(patterns[2], "%1" .. replacement)
155+
end
156+
return str, replacements
157+
end
158+
159+
function unescape_invalid_tags(str, tags)
160+
for replacement, k in pairs(tags) do
161+
-- replace all lua special replacement characters with their
162+
-- escaped versions, so that when we restore the behavior,
163+
-- we don't accidentally create a pattern
164+
local result = k:gsub("([$%%])", "%%%1")
165+
str = str:gsub(replacement, result)
166+
end
167+
return str
168+
end
57169

58170
local preprocess_qmd_filter = {
59-
171+
60172
-- search for `import_svelte("X.svelte")` refs in codeblocks and switch them
61173
-- to `import("X.js")`
62174
CodeBlock = function(block)
@@ -114,15 +226,62 @@ end
114226
-- transform each input qmd, saving the transformation in .sverto/[path]
115227
-- (write the identified .svelte files out to a file too!)
116228
for key, qmd_path in ipairs(in_files) do
117-
118-
local doc = pandoc.read(read_file(qmd_path))
229+
230+
-- before we read the file in with pandoc.read, let's read it in as a raw
231+
-- string and do the quarto team's qmd-reader processing on it. THEN we can
232+
-- read it with pandoc.read
233+
234+
print(">>> PROCESSING " .. qmd_path)
235+
236+
local raw_doc = read_file(qmd_path)
119237

120238
-- store the current qmd_path on disk so the filter can access it
121239
write_file(".sverto/.sverto-current-qmd-folder", path_dir(qmd_path))
240+
241+
-- escape invalid tags
242+
local txt, tags = escape_invalid_tags(tostring(raw_doc))
243+
244+
-- some extension + format stuff that we can maybe ignore?
245+
246+
-- for k, v in pairs(opts.extensions) do
247+
-- extensions[v] = true
248+
-- end
249+
250+
-- if param("user-defined-from") then
251+
-- local user_format = _quarto.format.parse_format(param("user-defined-from"))
252+
-- for k, v in pairs(user_format.extensions) do
253+
-- extensions[k] = v
254+
-- end
255+
-- end
256+
257+
-- -- Format flavor, i.e., which extensions should be enabled/disabled
258+
-- local flavor = {
259+
-- format = "markdown",
260+
-- extensions = extensions,
261+
-- }
262+
263+
local function restore_invalid_tags(tag)
264+
return tags[tag] or tag
265+
end
266+
267+
-- NOW we read in with pandoc (hopefully ending up with real code blocks)
268+
-- and restore them
269+
-- local doc = pandoc.read(txt, flavor, opts):walk {
270+
local doc = pandoc.read(txt, "markdown")
271+
272+
local restored_doc = doc:walk {
273+
CodeBlock = function (cb)
274+
cb.classes = cb.classes:map(restore_invalid_tags)
275+
cb.text = unescape_invalid_tags(cb.text, tags)
276+
return cb
277+
end
278+
}
279+
280+
-- local doc = pandoc.read(read_file(qmd_path))
122281

123282
-- pre-process the qmd, populating `svelte_files` in the process
124283
-- local svelte_files = {}
125-
local transformed_doc = doc:walk(preprocess_qmd_filter)
284+
local transformed_doc = restored_doc:walk(preprocess_qmd_filter)
126285
create_dir_recursively(".sverto/" .. path_dir(qmd_path))
127286
write_file(".sverto/" .. qmd_path, pandoc.write(transformed_doc, "markdown"))
128287

@@ -132,3 +291,4 @@ end
132291
write_file(".sverto/.sverto-outdir", os.getenv("QUARTO_PROJECT_OUTPUT_DIR"))
133292

134293
-- TODO - if there's no {{< import .sverto/file.qmd >}} block, add it?
294+

0 commit comments

Comments
 (0)