├── docs
├── .gitignore
├── articles
│ ├── index.qmd
│ ├── default-prop-values
│ │ └── index.qmd
│ └── migrating-from-sverto-v1
│ │ └── index.qmd
├── README.md
├── package.json
├── copy-extension.sh
├── Circles.svelte
├── _quarto.yml
├── style.scss
├── news.qmd
├── examples
│ ├── barchart
│ │ ├── BarChart.svelte
│ │ └── index.qmd
│ └── time-series
│ │ ├── TimeSeriesChart.svelte
│ │ └── index.qmd
├── index.qmd
└── package-lock.json
├── .quartoignore
├── _extensions
└── quarto-svelte
│ ├── _extension.yml
│ ├── rollup.config.js
│ ├── quarto-svelte-prerender.lua
│ ├── quarto-svelte.lua
│ ├── util.lua
│ └── quarto-svelte-logo.svg
├── package.json
├── .gitignore
├── LICENSE
├── Circles.svelte
├── example.qmd
├── .github
└── workflows
│ ├── check.yml
│ └── publish.yml
├── NEWS.md
└── README.md
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | /.quarto/
2 |
3 | /node_modules/
4 | /.quarto-svelte/
--------------------------------------------------------------------------------
/.quartoignore:
--------------------------------------------------------------------------------
1 | README.md
2 | NEWS.md
3 | LICENSE
4 | .luarc.json
5 | package-lock.json
6 | docs/
7 | .quarto/
8 | .github/
9 | !.gitignore
--------------------------------------------------------------------------------
/docs/articles/index.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Articles"
3 | description: In-depth use of quarto-svelte.
4 | listing:
5 | contents:
6 | - "*/index.qmd"
7 | ---
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # quarto-svelte documentation website
2 |
3 | Render (getting the latest copy of quarto-svelte) and preview using:
4 |
5 | ```bash
6 | ./copy-extension.sh && quarto render
7 |
8 | quarto preview
9 | ```
10 |
--------------------------------------------------------------------------------
/_extensions/quarto-svelte/_extension.yml:
--------------------------------------------------------------------------------
1 | title: quarto-svelte
2 | author: James Goldie
3 | version: 2.0.1
4 | quarto-version: ">=1.5.46"
5 | contributes:
6 | filters:
7 | - quarto-svelte.lua
8 | metadata:
9 | project:
10 | pre-render: quarto-svelte-prerender.lua
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-app",
3 | "version": "2.0.0",
4 | "type": "module",
5 | "private": true,
6 | "scripts": {
7 | "build": "rollup -c",
8 | "dev": "rollup -w -c"
9 | },
10 | "devDependencies": {
11 | "@rollup/plugin-commonjs": "^28.0.0",
12 | "@rollup/plugin-terser": "^0.4.4",
13 | "@rollup/plugin-node-resolve": "^16.0.0",
14 | "rollup": "^4.22.0",
15 | "rollup-plugin-svelte": "^7.2.2",
16 | "svelte": "^5.0.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-app",
3 | "version": "2.0.0",
4 | "type": "module",
5 | "private": true,
6 | "scripts": {
7 | "build": "rollup -c",
8 | "dev": "rollup -w -c"
9 | },
10 | "devDependencies": {
11 | "@rollup/plugin-commonjs": "^28.0.0",
12 | "@rollup/plugin-node-resolve": "^16.0.0",
13 | "@rollup/plugin-terser": "^0.4.4",
14 | "rollup": "^4.22.0",
15 | "rollup-plugin-svelte": "^7.2.2",
16 | "svelte": "^5.0.0"
17 | },
18 | "dependencies": {
19 | "d3": "^7.9.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/docs/copy-extension.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # from github.com/qmd-lab/closeread
4 | # (but this can't be run as a project pre-render because it's a custom project
5 | # type... so the extension needs to already be present!)
6 |
7 | mkdir -p _extensions/
8 | cp -Rf ../_extensions/quarto-svelte _extensions/
9 | echo "> quarto-svelte extension retrieved"
10 |
11 | if [ ! -f ./package.json ]; then
12 | echo "> quarto-svelte package.json not found; also copying"
13 | cp ../package.json package.json
14 | fi
15 |
16 | npm install
17 | echo "> NPM packes installed"
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # quarto backend folder
2 | /.quarto/
3 |
4 | # .quarto-svelte directory (except rollup.config.js)
5 | /.quarto-svelte/import
6 | /.quarto-svelte/.quarto-svelte-*
7 | !/.quarto-svelte/rollup.config.js
8 |
9 | # installed npm packages
10 | /node_modules/
11 |
12 | # system files
13 | .DS_Store
14 |
15 | # lua
16 | /.luarc.json
17 |
18 | # rendered documentation site
19 | /docs/_site/
20 | /docs/.quarto/
21 | /docs/.quarto-svelte/
22 |
23 | # quarto-svelte extension + files in docs site (we get it from project root on render)
24 | /docs/_extensions/quarto-svelte
25 | /docs/package.json
26 | /docs/package-lock.json
27 | /example_files/
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 360info
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Circles.svelte:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
21 |
22 |
23 | {@debug data}
24 |
25 |
28 |
38 |
--------------------------------------------------------------------------------
/docs/Circles.svelte:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
25 |
26 |
27 | {@debug data}
28 |
29 |
32 |
42 |
--------------------------------------------------------------------------------
/example.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: quarto-svelte example
3 | filters:
4 | - quarto-svelte
5 | quarto-svelte:
6 | use:
7 | - Circles.svelte
8 | ---
9 |
10 | This document shows you how to use a basic example Svelte component called `Circles.svelte`.
11 |
12 | Create one using an HTML template:
13 |
14 | ```{ojs}
15 | myCircles = html``
16 | ```
17 |
18 | Now the fun part - updating our visual when our document changes. It's as simple as changing the prop using `componentName.propName =`.
19 |
20 | For example, here are three datasets and an [Observable Inputs](https://observablehq.com/documentation/inputs/overview) dropdown menu that lets you select one of three datasets:
21 |
22 | ```{ojs}
23 | // here are some datasets...
24 | allDatasets = new Map([
25 | ["Dataset A", [5, 15, 25, 17, 8]],
26 | ["Dataset B", [25, 5, 5]],
27 | ["Dataset C", [12, 5, 8, 21, 5]]
28 | ]);
29 | ```
30 |
31 | ```{ojs}
32 | viewof selectedDataset =
33 | Inputs.select(allDatasets, { label: "Selected dataset" });
34 | ```
35 |
36 | Now we can update the prop `data` of the visual `myCircles` using:
37 |
38 | ```{ojs}
39 | //| output: false
40 | myCircles.data = selectedDataset
41 | ```
42 |
43 | And there you go! 🚀
44 |
45 | For more help writing Svelte components, check out the [Svelte tutorial](https://svelte.dev/tutorial/basics) or take look at some of the examples in the [quarto-svelte documentation](https://quarto-svelte.jamesgoldie.dev).
46 |
47 |
--------------------------------------------------------------------------------
/docs/_quarto.yml:
--------------------------------------------------------------------------------
1 | project:
2 | type: website
3 |
4 | execute:
5 | freeze: auto
6 |
7 | website:
8 | announcement:
9 | icon: stars
10 | dismissable: true
11 | type: warning
12 | position: above-navbar
13 | content: "Sverto is now quarto-svelte — and it supports Svelte 5! [**Learn how to migrate to the new version.**](/articles/migrating-from-sverto-v1){style=\"color: var(--bs-text-warning)\"}"
14 | navbar:
15 | background: "#ff3e00"
16 | foreground: "white"
17 | search: true
18 | left:
19 | - text: "quarto-svelte"
20 | file: index.qmd
21 | - text: Examples
22 | menu:
23 | - text: "Simple bar chart"
24 | file: examples/barchart/index.qmd
25 | - text: "Time series chart"
26 | file: examples/time-series/index.qmd
27 | - text: Articles
28 | menu:
29 | - text: Migrating from Sverto v1 and Svelte 3
30 | file: articles/migrating-from-sverto-v1
31 | - text: Default prop values
32 | file: articles/default-prop-values/index.qmd
33 | - text: News
34 | file: news.qmd
35 | right:
36 | - icon: github
37 | href: https://github.com/jimjam-slam/quarto-svelte
38 | page-footer: "The developers of Svelte are not affiliated with quarto-svelte."
39 |
40 | format:
41 | html:
42 | title-block-banner: "#222222"
43 | theme: [default, style.scss]
44 | linkcolor: "#ff3e00"
45 | callout-appearance: simple
46 |
47 |
--------------------------------------------------------------------------------
/.github/workflows/check.yml:
--------------------------------------------------------------------------------
1 | # workflow to test quarto-svelte
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches: [main, dev]
7 | pull_request:
8 | branches: [main, dev]
9 |
10 | name: quarto-svelte-check
11 |
12 | jobs:
13 | quarto-svelte-check:
14 |
15 | strategy:
16 | matrix:
17 | os: ["ubuntu-latest", "windows-latest"]
18 | quarto: ["release", "pre-release"]
19 |
20 | runs-on: ${{ matrix.os }}
21 |
22 | env:
23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
24 |
25 | steps:
26 | - uses: actions/checkout@v4
27 |
28 | # check on both latest and prerelease
29 | - name: Install Quarto
30 | uses: quarto-dev/quarto-actions/setup@v2
31 | with:
32 | version: ${{ matrix.quarto }}
33 |
34 | - name: Install Node
35 | uses: actions/setup-node@v4
36 | with:
37 | node-version: 20
38 |
39 | - name: Copy quarto-evelte extension; run npm install
40 | run: ./copy-extension.sh
41 | working-directory: ./docs
42 | shell: bash
43 |
44 | - name: Render quarto-svelte docs
45 | uses: quarto-dev/quarto-actions/render@v2
46 | with:
47 | path: docs
48 |
49 | - name: Verify that compiled Svelte .js bundles are present
50 | shell: bash
51 | run: |
52 | test -f docs/_site/Circles.js &&
53 | test -f docs/_site/examples/barchart/BarChart.js &&
54 | test -f docs/_site/examples/time-series/TimeSeriesChart.js
55 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | # workflow to build docs site and publish to github pages
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches: main
7 |
8 | name: quarto-svelte-publish
9 |
10 | permissions:
11 | contents: read
12 | pages: write
13 | id-token: write
14 |
15 | concurrency:
16 | group: "pages"
17 | cancel-in-progress: false
18 |
19 | jobs:
20 | build-docs:
21 | runs-on: "ubuntu-latest"
22 | env:
23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
24 | steps:
25 |
26 | - uses: actions/checkout@v4
27 |
28 | # use latest release version to publish docs
29 | - name: Install Quarto
30 | uses: quarto-dev/quarto-actions/setup@v2
31 | with:
32 | version: "release"
33 |
34 | - name: Install Node
35 | uses: actions/setup-node@v4
36 | with:
37 | node-version: 20
38 |
39 | - name: Copy quarto-evelte extension; run npm install
40 | run: ./copy-extension.sh
41 | working-directory: ./docs
42 | shell: bash
43 |
44 | - name: Render quarto-svelte docs
45 | uses: quarto-dev/quarto-actions/render@v2
46 | with:
47 | path: docs
48 |
49 | - name: Verify that compiled Svelte .js bundles are present
50 | shell: bash
51 | run: |
52 | test -f docs/_site/Circles.js &&
53 | test -f docs/_site/examples/barchart/BarChart.js &&
54 | test -f docs/_site/examples/time-series/TimeSeriesChart.js
55 |
56 | - name: Setup Pages
57 | uses: actions/configure-pages@v5
58 |
59 | - name: Upload docs/ artifact
60 | uses: actions/upload-pages-artifact@v3
61 | with:
62 | path: "./docs/_site"
63 |
64 | - name: Deploy to GitHub Pages
65 | id: deployment
66 | uses: actions/deploy-pages@v4
67 |
68 |
--------------------------------------------------------------------------------
/_extensions/quarto-svelte/rollup.config.js:
--------------------------------------------------------------------------------
1 | import svelte from "rollup-plugin-svelte"
2 | import commonjs from "@rollup/plugin-commonjs"
3 | import { nodeResolve } from "@rollup/plugin-node-resolve"
4 | import terser from "@rollup/plugin-terser"
5 |
6 | const path = require("node:path")
7 |
8 | // this is false when we run rollup with -w/--watch (never presently)
9 | const production = !process.env.ROLLUP_WATCH
10 |
11 | /* export an array of rollup configs - one for each input svelte file - using
12 | additional command line args supplied from lua */
13 | export default cmd => {
14 |
15 | const svelteInputPaths =
16 | cmd["configSvelteInPaths"].split(":").filter(d => d != "")
17 |
18 | // if no svelte paths, bail out early
19 | if (svelteInputPaths == undefined || svelteInputPaths.length == 0) {
20 | console.log("No Svelte filtes found; skipping Svelte compilation")
21 | process.exit(0)
22 | }
23 |
24 | /* get quarto render dir from cmd line arg */
25 | const quartoRenderPath = cmd["configQuartoOutPath"]
26 | if (quartoRenderPath == undefined) {
27 | console.error(
28 | "Error: supply a --configQuartoOutPath. Please report this to " +
29 | "the quarto-svelte developer.")
30 | process.exit(1)
31 | }
32 |
33 | // send a rollup config for each svelte file
34 | return svelteInputPaths.map(
35 |
36 | (svelteFile, index) => ({
37 | input: svelteFile,
38 | output: {
39 | format: "es",
40 | dir: path.join(quartoRenderPath, path.dirname(svelteFile)),
41 | sourcemap: true
42 | },
43 | plugins: [
44 | svelte({
45 | // css is added to the js bundle instead
46 | emitCss: false,
47 | compilerOptions: {
48 | // required for ojs reactivity
49 | accessors: true,
50 | customElement: true,
51 | dev: !production,
52 | }
53 | }),
54 | nodeResolve({
55 | browser: true,
56 | dedupe: ["svelte"]
57 | }),
58 | commonjs(),
59 | production && terser()
60 | ]
61 | })
62 |
63 | )
64 |
65 | }
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/docs/style.scss:
--------------------------------------------------------------------------------
1 | /*-- scss:defaults --*/
2 |
3 | /*-- scss:rules --*/
4 |
5 | .btn {
6 | box-shadow:
7 | 0 .2em .8em .2em rgba(0,0,0,.05),
8 | 0 .2em .6em .1em rgba(0,0,0,.025);
9 | }
10 |
11 | /* svelte code blocks (no syntax highlighting) */
12 |
13 | .code-with-filename {
14 | margin: 1em 0;
15 | }
16 |
17 | .svelte.sourceCode {
18 | background-color: rgba(233,236,239,.65);
19 | border: 1px solid rgba(233,236,239,.65);
20 | border-radius: .25rem;
21 | color: #003b4f;
22 | }
23 |
24 | .code-with-filename .code-with-filename-file strong {
25 | color: var(--quarto-scss-export-text-muted, "slategrey");
26 | }
27 |
28 | @include media-breakpoint-up(md) {
29 | body .quarto-title-block .quarto-title-banner {
30 | background: radial-gradient(circle at 20%,
31 | #222,
32 | #222 calc(520px),
33 | white calc(520px),
34 | white calc(520px + 5%),
35 | #ff3e00 calc(520px + 5%),
36 | #ff3e00 calc(520px + 10%),
37 | #75A9DB calc(520px + 10%),
38 | #75A9DB calc(520px + 15%),
39 | #222 calc(520px + 15%));
40 | }
41 | }
42 |
43 | @include media-breakpoint-up(lg) {
44 | body .quarto-title-block .quarto-title-banner {
45 | background: radial-gradient(circle at 50%,
46 | #222,
47 | #222 calc(420px),
48 | white calc(420px),
49 | white calc(420px + 30px),
50 | #ff3e00 calc(420px + 30px),
51 | #ff3e00 calc(420px + 60px),
52 | #75A9DB calc(420px + 60px),
53 | #75A9DB calc(420px + 90px),
54 | #222 calc(420px + 90px));
55 | }
56 | }
57 | @include media-breakpoint-up(lg) {
58 | body:has(main.page-full) .quarto-title-block .quarto-title-banner {
59 | background: radial-gradient(circle at 42.5%,
60 | #222,
61 | #222 calc(420px),
62 | white calc(420px),
63 | white calc(420px + 30px),
64 | #ff3e00 calc(420px + 30px),
65 | #ff3e00 calc(420px + 60px),
66 | #75A9DB calc(420px + 60px),
67 | #75A9DB calc(420px + 90px),
68 | #222 calc(420px + 90px));
69 | }
70 | }
--------------------------------------------------------------------------------
/_extensions/quarto-svelte/quarto-svelte-prerender.lua:
--------------------------------------------------------------------------------
1 | -- quarto-svelte-prerender.lua
2 | -- james goldie
3 | -- this pre-render script runs when quarto-svelte is installed on a quarto project. it:
4 | -- 1) looks in the doc meta for svelte paths
5 | -- 2) calls the svelte compiler with the svelte paths identified in (1)
6 | -- (injecting the bundled svelte files into the html is handled by quarto-svelte.lua)
7 |
8 | print(">>> quarto-svelte: project pre-render")
9 |
10 | -- try to load a module from multiple paths
11 | -- modified version of https://stackoverflow.com/a/17878208/3246758
12 | local function try_require(path_table)
13 | for _, v in pairs(path_table) do
14 | ok, err = pcall(require, v)
15 | if ok then
16 | return err
17 | end
18 | end
19 | error("Could not find a module to load from selected paths")
20 | end
21 |
22 | util = try_require({
23 | "_extensions.jimjam-slam.quarto-svelte.util",
24 | "_extensions.quarto-svelte.util",
25 | })
26 |
27 | input_paths = os.getenv("QUARTO_PROJECT_INPUT_FILES")
28 |
29 | -- walk input files, processing the meta to get the svelte files
30 | -- (make relative paths relative to the input file!)
31 |
32 | svelte_paths = {}
33 |
34 | -- for _, input_path in ipairs(input_paths) do
35 | for input_path in input_paths:gmatch("[^\n]+") do
36 |
37 | local doc = pandoc.read(util.read_file(input_path), "markdown")
38 |
39 | doc:walk {
40 | Meta = function(m)
41 |
42 | -- confirm quarto-svelte.use is a string or list
43 | if m["quarto-svelte"] == nil or m["quarto-svelte"].use == nil then
44 | return nil
45 | end
46 |
47 | local quarto_svelte_use = util.get_svelte_paths_from_meta(m)
48 | -- add each unique path, resolving relative project location
49 | for _, svelte_path in ipairs(quarto_svelte_use) do
50 | local offset_path = util.offset_svelte_path(
51 | pandoc.utils.stringify(svelte_path),
52 | input_path)
53 | svelte_paths[offset_path] = offset_path
54 | end
55 |
56 | end
57 | }
58 |
59 | end
60 |
61 | -- now concat .svelte paths
62 | local svelte_path_string = ""
63 | for _, svelte_path in pairs(svelte_paths) do
64 | svelte_path_string = svelte_path_string .. ":" .. svelte_path
65 | end
66 |
67 | util.compile_svelte_files(
68 | os.getenv("QUARTO_PROJECT_OUTPUT_DIR"),
69 | svelte_path_string)
70 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 | ## quarto-svelte 2.0.1
2 |
3 | - Improvement: extraneous OJS import code is removed from bootstrapping script
4 | - Fix errors in GitHub Actions workflows that prevented examples in the documentation site from appearing. GHA actions now run on latest Quarto release (check workflow also runs on latest prerelease).
5 |
6 | ## quarto-svelte 2.0.0
7 |
8 | - Rename Sverto to quarto-svelte
9 | - **Breaking:** `filters: ["sverto"]` is now `filters: ["quarto-svelte"]`
10 | - **Breaking:** `sverto.use` is now `quarto-svelte.use`
11 | - Repo name is now `jimjam-slam/quarto-svelte`, but `jimjam-slam/sverto` will continue to work
12 | - Upgrade from Svelte 3 to Svelte 5
13 | - For notes on rewriting existing Svelte components for Svelte 5, see the [Svelte 5 Migration Guide](https://svelte.dev/docs/svelte/v5-migration-guide)
14 | - Now uses web components
15 | - **Breaking:** in addition to general Svelte 5 migration, components for quarto-svelte need to have a [custom element option](https://svelte.dev/docs/svelte/custom-elements)
16 | - **Breaking:** old instantiation syntax no longer works in Svelte 5. Instead, use an HTML template or JavaScript to create an element with the custom element name
17 |
18 | ## Sverto 1.0.0
19 |
20 | - Significant refactor of Sverto makes it easier to use and more compatible with Quarto's other features
21 | - Use Sverto in a Quarto document by adding `sverto` to `filters` in the document frontmatter
22 | - Add Svelte files to a document using the frontmatter key `sverto.use`
23 | - No need for magic blocks anymore!
24 | - When working in a website project, optionally use the `sverto` project type to cut down on duplicate Svelte compilation Quarto documents
25 | - Works properly with Quarto includes
26 | - Requires Quarto pre-release 1.5.25 or higher on Windows, but should work fine on Quarto 1.4 on macOS and Linux.
27 |
28 | ## Sverto 0.0.3
29 |
30 | - Migrated from [`360-info/sverto`](https://github.comn/360-info/sverto) to [`jimjam-slam/sverto`](htps://github.com/jimjam-slam/sverto). Old GitHub links are maintained.
31 |
32 | ## Sverto 0.0.2
33 |
34 | - Bump minimum Quarto version to 1.3.0.
35 | - Fixes for compatibility with newer Quarto 1.3 pre-releases
36 | - 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.
37 | - We now take advantage of the improved `.quartoignore` functionality in Quarto 1.3 to:
38 | 1. avoid copying the `docs` folder in with the project template; and
39 | 2. include the `.gitignore` with the template
40 |
41 | ## Sverto 0.0.1
42 |
43 | - Initial release
44 |
--------------------------------------------------------------------------------
/docs/news.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: News
3 | date: last-modified
4 | ---
5 |
6 | ## quarto-svelte 2.0.1
7 |
8 | - Improvement: extraneous OJS import code is removed from bootstrapping script
9 | - Fix errors in GitHub Actions workflows that prevented examples in the documentation site from appearing. GHA actions now run on latest Quarto release (check workflow also runs on latest prerelease).
10 |
11 | ## quarto-svelte 2.0.0
12 |
13 | - Rename Sverto to quarto-svelte
14 | - **Breaking:** `filters: ["sverto"]` is now `filters: ["quarto-svelte"]`
15 | - **Breaking:** `sverto.use` is now `quarto-svelte.use`
16 | - Repo name is now `jimjam-slam/quarto-svelte`, but `jimjam-slam/sverto` will continue to work
17 | - Upgrade from Svelte 3 to Svelte 5
18 | - For notes on rewriting existing Svelte components for Svelte 5, see the [Svelte 5 Migration Guide](https://svelte.dev/docs/svelte/v5-migration-guide)
19 | - Now uses web components
20 | - **Breaking:** in addition to general Svelte 5 migration, components for quarto-svelte need to have a [custom element option](https://svelte.dev/docs/svelte/custom-elements)
21 | - **Breaking:** old instantiation syntax no longer works in Svelte 5. Instead, use an HTML template or JavaScript to create an element with the custom element name
22 |
23 | ## Sverto 1.0.0
24 |
25 | - Big refactor of sverto makes it easier to use and more compatible with Quarto's other features!
26 | - Use Sverto in a Quarto document by adding `sverto` to `filters` in the document frontmatter
27 | - Add Svelte files to a document using the frontmatter key `sverto.use`
28 | - No need for magic blocks anymore!
29 | - When working in a website project, optionally use the `sverto` project type to cut down on duplicate Svelte compilation Quarto documents
30 | - Works properly with Quarto includes
31 | - **Requires Quarto pre-release 1.5.25 or higher on Windows,** but should work fine on Quarto 1.4 on macOS and Linux.
32 |
33 | ## Sverto 0.0.3
34 |
35 | - Migrated from [`360-info/sverto`](https://github.comn/360-info/sverto) to [`jimjam-slam/sverto`](htps://github.com/jimjam-slam/sverto). Old GitHub links are maintained.
36 |
37 | ## Sverto 0.0.2
38 |
39 | - Bump minimum Quarto version to 1.3.0.
40 | - Fixes for compatibility with newer Quarto 1.3 pre-releases
41 | - 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.
42 | - We now take advantage of the improved `.quartoignore` functionality in Quarto 1.3 to:
43 | 1. avoid copying the `docs` folder in with the project template; and
44 | 2. include the `.gitignore` with the template
45 |
46 | ## 0.0.1
47 |
48 | - Initial release
49 |
--------------------------------------------------------------------------------
/docs/examples/barchart/BarChart.svelte:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
36 |
37 |
38 |
43 |
44 |
50 |
51 |
80 |
--------------------------------------------------------------------------------
/docs/examples/time-series/TimeSeriesChart.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
67 |
68 |
83 |
84 |
85 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/_extensions/quarto-svelte/quarto-svelte.lua:
--------------------------------------------------------------------------------
1 | -- quarto-svelte.lua
2 | -- james goldie
3 | -- this filter runs on individual documents when it's specified. it handles 2–3
4 | -- things:
5 | -- 1) looks in the doc meta for svelte paths
6 | -- 2) adds a vanilla js block to the doc header to initialise the compiled svelte
7 | -- 3) IF the filter is NOT running in a project context (ie. a single doc),
8 | -- call the svelte compiler with the svelte paths identified in (1)
9 | -- (if it is a project, this step is handled by quarto-svelte-prerender.lua across
10 | -- the whole project)
11 |
12 | quarto.log.info(">>> quarto-svelte: document filter")
13 |
14 | util = require("./util")
15 |
16 | -- inject_svelte: a pandoc filter that extracts svelte files named in the doc
17 | -- frontmatter and adds javascript blocks to import them as es modules
18 | function inject_svelte_and_compile(m)
19 |
20 | if not quarto.doc.isFormat("html") then
21 | quarto.log.warning("quarto-svelte shortcode ignored for non-HTML output")
22 | return nil
23 | end
24 |
25 | -- no files to process? abort
26 | if m["quarto-svelte"] == nil or m["quarto-svelte"].use == nil then
27 | quarto.log.warning(
28 | "No Svelte files found under `quarto-svelte.use`. " ..
29 | "To use quarto-svelte with this document, add a list of .svelte files " .."to the document frontmatter under the `quarto-svelte.use` key.\n\n" ..
30 | "If you did not intend to use quarto-svelte on this document, " ..
31 | "consider removing the quarto-svelte filter (you can still use it on " ..
32 | "other pages of a Quarto website).")
33 | return nil
34 | end
35 |
36 | -- either add text to start of body (and return nil), or return a rawblock
37 | -- %s: compiled svelte js path
38 | -- %s: obj_name
39 | local svelte_js_import_template = ''
40 |
41 | -- abort if quarto-svelte.use is not a string or a list of MetaInlines
42 | local quarto_svelte_use = util.get_svelte_paths_from_meta(m)
43 |
44 | -- now inject the ojs init code for the user's svelte bundles while
45 | -- building a list of .svelte files to potentially compile
46 | local svelte_paths_string = ""
47 | for _, path in ipairs(quarto_svelte_use) do
48 | -- this is where we process the other .svelte paths
49 | local in_path = pandoc.utils.stringify(path)
50 | local in_dir = pandoc.path.directory(in_path)
51 | local in_name = pandoc.path.filename(in_path)
52 | local obj_name = pandoc.path.split_extension(in_name)
53 | -- bundle path should use *nix separators for web, not platform default
54 | local compiled_path = pandoc.path.join({ in_dir, obj_name .. ".js" })
55 | local web_path = compiled_path:gsub("\\", "/")
56 |
57 | -- add .svelte path to svelte compiler path list...
58 | svelte_paths_string = svelte_paths_string .. in_path .. ":"
59 |
60 | -- ... and inject the ojs init code for it
61 | local svelte_insert = string.format(svelte_js_import_template,
62 | web_path)
63 | quarto.doc.include_text("before-body", svelte_insert)
64 |
65 | -- finally, if we're rendering a single doc (not in a project),
66 | -- compile the svelte file to a js bundle
67 | if quarto.project.directory ~= nil then
68 | quarto.log.debug("Project found; deferring Svelte compilation to pre-render script")
69 | else
70 | util.compile_svelte_files("./", svelte_paths_string)
71 | end
72 | end
73 | end
74 |
75 | return {
76 | Meta = inject_svelte_and_compile
77 | }
78 |
--------------------------------------------------------------------------------
/_extensions/quarto-svelte/util.lua:
--------------------------------------------------------------------------------
1 |
2 | -- return contents of named file
3 | function read_file(name)
4 | local file = io.open(name, "r")
5 | if file == nil then
6 | return ""
7 | end
8 | local contents = file:read("a")
9 | file:close()
10 | return contents
11 | end
12 |
13 | -- get a prefix for calling npm run based on windows or *nix/macos
14 | -- using path separator: i don't know pandoc.system.os() values
15 | function get_cmd_prefix()
16 | if pandoc.path.separator == "\\" then
17 | return "cmd /c "
18 | else
19 | return ""
20 | end
21 | end
22 |
23 | -- file_exists: true if the file at `name` exists
24 | -- from https://pandoc.org/lua-filters.html#building-images-with-tikz
25 | function file_exists(name)
26 | local f = io.open(name, 'r')
27 | if f ~= nil then
28 | io.close(f)
29 | return true
30 | else
31 | return false
32 | end
33 | end
34 |
35 | -- offset a relative `svelte_path` to a .qmd `input_path`, or an absolute
36 | -- `svelte_path` to the project path. then normalize.
37 | function offset_svelte_path(svelte_path, input_path)
38 |
39 | -- offset from either input .qmd (if relative) or project dir (if absolute)
40 | -- local offset_from = pandoc.system.get_working_directory()
41 | local offset_from = "./"
42 | if pandoc.path.is_relative(svelte_path) then
43 | -- offset_from = pandoc.path.directory(input_path)
44 | offset_from = pandoc.path.join({
45 | offset_from,
46 | pandoc.path.directory(input_path)
47 | })
48 | end
49 |
50 | -- join offset and svelt paths
51 | local relative_path = pandoc.path.join({
52 | offset_from,
53 | pandoc.utils.stringify(svelte_path)
54 | })
55 |
56 | -- normalize and return
57 | local final_path = pandoc.path.normalize(relative_path)
58 | return final_path
59 | end
60 |
61 | function get_svelte_paths_from_meta(m)
62 | local quarto_svelte_use
63 | if pandoc.utils.type(m["quarto-svelte"].use) == "List" then
64 | quarto_svelte_use = m["quarto-svelte"].use
65 | elseif type(m["quarto-svelte"].use) == "string" then
66 | quarto_svelte_use = { m["quarto-svelte"].use }
67 | else
68 | print(
69 | "[\"quarto-svelte\"].use should be Inlines, not " ..
70 | pandoc.utils.type(m["quarto-svelte"].use))
71 | end
72 | return quarto_svelte_use
73 | end
74 |
75 | function compile_svelte_files(quarto_out_path, svelte_paths_string)
76 | -- first, find the rollup config
77 | local rollup_config
78 | if file_exists("./_extensions/jimjam-slam/quarto-svelte/rollup.config.js") then
79 | rollup_config = "./_extensions/jimjam-slam/quarto-svelte/rollup.config.js"
80 | elseif file_exists("./_extensions/quarto-svelte/rollup.config.js") then
81 | rollup_config = "./_extensions/quarto-svelte/rollup.config.js"
82 | else
83 | print("Error: quarto-svelte extension files not found. " ..
84 | "Is quarto-svelte installed properly?")
85 | os.exit(1)
86 | end
87 |
88 | local svelte_command =
89 | "npm run build " .. rollup_config .. " -- " ..
90 | '--configQuartoOutPath="' .. quarto_out_path .. '" ' ..
91 | '--configSvelteInPaths="' .. svelte_paths_string .. '" ' ..
92 | '--bundleConfigAsCjs'
93 |
94 | local rollup_result = os.execute(svelte_command)
95 | if rollup_result == nil or rollup_result == true then
96 | print(">>> quarto-svelte: Svelte compiler finished!")
97 | else
98 | print(">>> quarto-svelte: Svelte compiler exited with code " .. tostring(rollup_result))
99 | end
100 | end
101 |
102 | return {
103 | read_file = read_file,
104 | get_cmd_prefix = get_cmd_prefix,
105 | file_exists = file_exists,
106 | offset_svelte_path = offset_svelte_path,
107 | get_svelte_paths_from_meta = get_svelte_paths_from_meta,
108 | compile_svelte_files = compile_svelte_files }
--------------------------------------------------------------------------------
/docs/articles/default-prop-values/index.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Default prop values
3 | description: More advanced ways to instantiate your Svelte components and pass props to them.
4 | ---
5 |
6 | When you place your Svelte component on the page, it will be instantiated (created) with initial values for its props — either the default prop values written into the component, or initial values that you give it.
7 |
8 | For example, the `Circles.svelte` component on this site's home page has a prop, `data`:
9 |
10 | ```{.svelte .sourceCode filename="Circles.svelte"}
11 | let { data = [5, 15, 10, 12, 14] } = $props();
12 | ```
13 |
14 | If you create `` without supplying `data`, that prop will first be `[5, 15, 10, 12, 14]`:
15 |
16 | ```{ojs}
17 | //| eval: false
18 | myCircles = html`
19 |
20 | `
21 | ```
22 |
23 | But you could also create it your own initial value for `data`:
24 |
25 | ```{ojs}
26 | //| eval: false
27 | myCircles = html`
28 |
29 | `
30 | ```
31 |
32 | If you are also supplying prop values to a Svelte component via OJS, the component will begin at the initial value above and then switch to the OJS-supplied value as soon as it becomes available.
33 |
34 | That might be nearly immediately, so you may not notice the differnce. But if your component is near the top of the page and transitions elements slowly, you may notice the change — a bar chart might immediate transition from one set of bar chart heights to another, for example. You may want to supply initial props that make sense for your component.
35 |
36 | # Passing complex data as default props
37 |
38 | If you're supplying props to your Svelte component when you place it, you may notice that they're in quotes. Like regular HTML attributes, those props are strings.
39 |
40 | If you want a Svelte component to convert those props from strings to something else, you can specify that at the top of the component using [component options](https://svelte.dev/docs/svelte/custom-elements#Component-options).
41 |
42 | For example, the component options for `Circles.svelte` looks like this:
43 |
44 | ```{.svelte .sourceCode filename="Circles.svelte"}
45 |
51 | ```
52 |
53 | Instead of simply supplying a name, like `customElement="bar-chart"`, we provide an object with the configuration. The name of the element is the `tag` option, while prop configuration is under `props`.
54 |
55 | For each prop, you can provide a `type`—one of `'String'`, `'Boolean'`, `'Number'`, `'Array'` or `'Object'`. For our data prop, the type is set to `"Array"`, so the default value `"[1, 2, 3, 4, 5]"` is converted from a String to an array of numbers.
56 |
57 | ## Nested arrays and objects
58 |
59 | Nested arrays and objects are common in data science, where we might want to pass a data-frame like array through.
60 |
61 | For example, you might be tempted to try something like this:
62 |
63 | ```{ojs}
64 | //| eval: false
65 | myCircles = html`
66 |
68 | `
69 | ```
70 |
71 | This, unfortunately, won't work, even if you've set the prop type to `"Array"` or `"Object"`.
72 |
73 | But there is also an alternate way of placing your element with JavaScript that allows you to pass nested arrays and objects as default props:
74 |
75 | ```{ojs}
76 | //| eval: false
77 | anotherBarChart = {
78 | const chart = document.createElement("bar-chart")
79 | chart.data = [
80 | { name: "James", score: 13 },
81 | { name: "Andrew", score: 15 } ]
82 | return chart
83 | }
84 | ```
85 |
86 | Of course, you can always make your default value an empty array (or make the default value inside the component an empty array) and then pass the nested one via OJS:
87 |
88 | ```{ojs}
89 | //| eval: false
90 | myCircles = html`
91 |
92 | `
93 | ```
94 |
95 |
96 | ```{ojs}
97 | //| eval: false
98 | myCircles.data = [
99 | { name: "James", score: 13 },
100 | { name: "Andrew", score: 15 } ]
101 | ```
--------------------------------------------------------------------------------
/docs/articles/migrating-from-sverto-v1/index.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Migrating from Sverto v1 and Svelte 3
3 | date: 2025-06-12
4 | ---
5 |
6 | If you've already been using Sverto v1, there are a few changes you'll need to make to update to quarto-svelte v2.
7 |
8 | # Update the extension
9 |
10 | Because the name of the extension has changed, I recommend deleting `_extensions/jimjam-slam/sverto` (or `_extensions/sverto`, if you installed from a ZIP file) from your project.
11 |
12 | Then, from the root of your project (or the same folder as your doc, if you aren't using a project) run:
13 |
14 | ```bash
15 | quarto use template jimjam-slam/quarto-svelte
16 | ```
17 |
18 | # (For projects) Remove the custom project type
19 |
20 | Previously, we used `project.type: sverto` instead of `project.type: website` in `_quarto.yml`. This meant that you couldn't use Sverto with other custom project types.
21 |
22 | This is no longer the case: quarto-svelte v2 operates as a [metadata extension](https://quarto.org/docs/extensions/metadata.html), so its project prerender script will work automatically whenever it's installed in a Quarto project. You can revert the project type to `website`, or to another custom project type if you have other extensions installed.
23 |
24 | # Migrate components
25 |
26 | Version 2 of quarto-svelte moves from Svelte 3 to Svelte 5.
27 |
28 | This is a substantial change even to basic components, and your Svelte components will need to be updated for the change. Start with the [Svelte 5 migration guide](https://svelte.dev/docs/svelte/v5-migration-guide) to get a sense of the changes you need to make.
29 |
30 | Thankfully, Svelte also provides a [migration script](https://svelte.dev/docs/svelte/v5-migration-guide#Migration-script) that can automatically update old components.
31 |
32 | To run the migration tool, from your project or document folder run:
33 |
34 | ```bash
35 | npx sv migrate svelte-5
36 | ```
37 |
38 | # Add custom element options to components
39 |
40 | As well as migrating components to Svelte 5, you will need to configure them as [custom elements](https://svelte.dev/docs/svelte/custom-elements).
41 |
42 | Using custom elements allows Svelte components to be placed on the page as if they were HTML elements. It also allows quarto-svelte to sidestep difficult changes to the way Svelte components are normally placed on pages.
43 |
44 | Unfortunately, this configuration isn't something that can be done automatically — you'll need to provide, at minimum, a name for your custom element by providing a block like the following at the top of your Svelte component (before the `