├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ └── feature-request.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── release.yml │ └── tests.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cli.ts ├── cli ├── build.ts ├── build_worker.ts ├── cms.ts ├── cms_worker.ts ├── create.ts ├── missing_worker_apis.ts ├── run.ts ├── upgrade.ts └── utils.ts ├── core ├── cache.ts ├── components.ts ├── data_loader.ts ├── debugbar.ts ├── events.ts ├── file.ts ├── formats.ts ├── fs.ts ├── loaders │ ├── binary.ts │ ├── json.ts │ ├── module.ts │ ├── text.ts │ ├── toml.ts │ └── yaml.ts ├── processors.ts ├── renderer.ts ├── scopes.ts ├── scripts.ts ├── searcher.ts ├── server.ts ├── site.ts ├── slugifier.ts ├── source.ts ├── utils │ ├── cli_options.ts │ ├── concurrent.ts │ ├── css_urls.ts │ ├── data_values.ts │ ├── date.ts │ ├── digest.ts │ ├── dom.ts │ ├── dom_links.ts │ ├── env.ts │ ├── format.ts │ ├── generator.ts │ ├── log.ts │ ├── lume_config.ts │ ├── lume_version.ts │ ├── merge_data.ts │ ├── net.ts │ ├── object.ts │ ├── page_content.ts │ ├── page_date.ts │ ├── page_url.ts │ ├── path.ts │ ├── read.ts │ └── tokens.ts ├── watcher.ts └── writer.ts ├── deno.json ├── deps ├── assert.ts ├── base64.ts ├── brotli.ts ├── cli.ts ├── cliffy.ts ├── cms.ts ├── colors.ts ├── crypto.ts ├── date.ts ├── debugbar.ts ├── decap.ts ├── dom.ts ├── esbuild.ts ├── eta.ts ├── fff.ts ├── front_matter.ts ├── fs.ts ├── hex.ts ├── highlight.ts ├── http.ts ├── icons.ts ├── init.ts ├── jsonc.ts ├── katex-auto-render │ ├── LICENSE │ ├── auto-render.ts │ └── splitAtDelimiters.js ├── katex.ts ├── lightningcss.ts ├── markdown_it.ts ├── mdx.ts ├── media_types.ts ├── minify_html.ts ├── nunjucks.ts ├── outdent.ts ├── pagefind.ts ├── path.ts ├── postcss.ts ├── prism.ts ├── pug.ts ├── purgecss.ts ├── remark.ts ├── remove-markdown.ts ├── sass.ts ├── satori.ts ├── schema-dts.ts ├── sharp.ts ├── sheetjs.ts ├── snapshot.ts ├── ssx.ts ├── streams.ts ├── svg2png.ts ├── svgo.ts ├── tailwindcss.ts ├── terser.ts ├── toml.ts ├── unocss.ts ├── vento.ts ├── xml.ts └── yaml.ts ├── lint.ts ├── middlewares ├── basic_auth.ts ├── cache_busting.ts ├── expires.ts ├── logger.ts ├── no_cache.ts ├── no_cors.ts ├── not_found.ts ├── precompress.ts ├── redirect_as2.ts ├── redirects.ts ├── reload.ts ├── reload_client.js ├── router.ts ├── serve_folder.ts ├── shutdown.ts └── www.ts ├── mod.ts ├── plugins ├── attributes.ts ├── base_path.ts ├── brotli.ts ├── check_urls.ts ├── code_highlight.ts ├── date.ts ├── decap_cms.ts ├── esbuild.ts ├── eta.ts ├── extract_date.ts ├── favicon.ts ├── feed.ts ├── fff.ts ├── filter_pages.ts ├── google_fonts.ts ├── gzip.ts ├── icons.ts ├── inline.ts ├── json.ts ├── json_ld.ts ├── jsx.ts ├── katex.ts ├── lightningcss.ts ├── markdown.ts ├── mdx.ts ├── metas.ts ├── minify_html.ts ├── modify_urls.ts ├── modules.ts ├── multilanguage.ts ├── nav.ts ├── nunjucks.ts ├── og_images.ts ├── pagefind.ts ├── paginate.ts ├── picture.ts ├── plaintext.ts ├── postcss.ts ├── prism.ts ├── pug.ts ├── purgecss.ts ├── reading_info.ts ├── redirects.ts ├── relations.ts ├── relative_urls.ts ├── remark.ts ├── resolve_urls.ts ├── robots.ts ├── sass.ts ├── search.ts ├── sheets.ts ├── sitemap.ts ├── slugify_urls.ts ├── source_maps.ts ├── sri.ts ├── svgo.ts ├── tailwindcss.ts ├── terser.ts ├── toml.ts ├── transform_images.ts ├── unocss.ts ├── url.ts ├── vento.ts └── yaml.ts ├── tests ├── __snapshots__ │ ├── base_path.test.ts.snap │ ├── brotli.test.ts.snap │ ├── build.test.ts.snap │ ├── cache_busting.test.ts.snap │ ├── check_urls.test.ts.snap │ ├── code_highlight.test.ts.snap │ ├── components.test.ts.snap │ ├── decap_cms.test.ts.snap │ ├── esbuild.test.ts.snap │ ├── eta.test.ts.snap │ ├── extract_date.test.ts.snap │ ├── favicon.test.ts.snap │ ├── feed.test.ts.snap │ ├── fff.test.ts.snap │ ├── filter_pages.test.ts.snap │ ├── google_fonts.test.ts.snap │ ├── gzip.test.ts.snap │ ├── icons.test.ts.snap │ ├── inline.test.ts.snap │ ├── json.test.ts.snap │ ├── json_ld.test.ts.snap │ ├── jsx.test.ts.snap │ ├── katex.test.ts.snap │ ├── layout.test.ts.snap │ ├── lightningcss.test.ts.snap │ ├── loaders.test.ts.snap │ ├── markdown.test.ts.snap │ ├── mdx.test.ts.snap │ ├── metas.test.ts.snap │ ├── minify_html.test.ts.snap │ ├── module.test.ts.snap │ ├── multilanguage.test.ts.snap │ ├── nav.test.ts.snap │ ├── no_cache.test.ts.snap │ ├── no_cors.test.ts.snap │ ├── nunjucks.test.ts.snap │ ├── og_images.test.ts.snap │ ├── pagefind.test.ts.snap │ ├── picture.test.ts.snap │ ├── plaintext.test.ts.snap │ ├── postcss.test.ts.snap │ ├── pretty_urls.test.ts.snap │ ├── prism.test.ts.snap │ ├── pug.test.ts.snap │ ├── purgecss.test.ts.snap │ ├── reading_info.test.ts.snap │ ├── redirects.test.ts.snap │ ├── relations.test.ts.snap │ ├── relative_urls.test.ts.snap │ ├── remark.test.ts.snap │ ├── remote_files.test.ts.snap │ ├── render_order.test.ts.snap │ ├── resolve_urls.test.ts.snap │ ├── robots.test.ts.snap │ ├── router.test.ts.snap │ ├── sass.test.ts.snap │ ├── search.test.ts.snap │ ├── sheets.test.ts.snap │ ├── sitemap.test.ts.snap │ ├── slugify_urls.test.ts.snap │ ├── source_maps.test.ts.snap │ ├── sri.test.ts.snap │ ├── static_files.test.ts.snap │ ├── svgo.test.ts.snap │ ├── symlinks.test.ts.snap │ ├── tailwindcss.test.ts.snap │ ├── terser.test.ts.snap │ ├── toml.test.ts.snap │ ├── transform_images.test.ts.snap │ ├── unocss.test.ts.snap │ ├── url.test.ts.snap │ ├── vento.test.ts.snap │ ├── well_known.test.ts.snap │ └── www.test.ts.snap ├── assets │ ├── base_path │ │ ├── index.vto │ │ └── styles.css │ ├── check_urls │ │ ├── index.md │ │ ├── page1.md │ │ └── page4.md │ ├── code_highlight │ │ ├── index.md │ │ └── other.vto │ ├── components │ │ ├── _components │ │ │ ├── eta_button.eta │ │ │ ├── header │ │ │ │ ├── comp.vto │ │ │ │ ├── script.js │ │ │ │ └── style.css │ │ │ ├── inherit.njk │ │ │ ├── jsx_button.jsx │ │ │ ├── njk_button.njk │ │ │ ├── not_inherit.njk │ │ │ └── vento_button.vto │ │ ├── _data.yml │ │ ├── index.vto │ │ └── subfolder │ │ │ ├── _components │ │ │ ├── innerButton.js │ │ │ ├── pug_button.pug │ │ │ └── ts_button.ts │ │ │ └── index.njk │ ├── components_interop │ │ ├── _components │ │ │ ├── subtitle.vto │ │ │ └── title.tsx │ │ ├── index.vto │ │ └── index2.page.tsx │ ├── decap_cms │ │ ├── _data │ │ │ └── decap_cms.yml │ │ └── index.md │ ├── deno.jsonc │ ├── empty │ │ └── .gitkeep │ ├── esbuild │ │ ├── _includes │ │ │ └── layout.js │ │ ├── data.json │ │ ├── main.d.ts │ │ ├── main.ts │ │ ├── main.vto │ │ ├── modules │ │ │ └── to_uppercase.ts │ │ ├── other.page.ts │ │ └── other │ │ │ ├── _data.yml │ │ │ ├── script.ts │ │ │ └── to_lowercase.ts │ ├── esbuild_jsx │ │ ├── deno.json │ │ ├── index.page.jsx │ │ └── main.jsx │ ├── eta │ │ ├── _includes │ │ │ ├── footer.eta │ │ │ ├── header.eta │ │ │ └── layout.eta │ │ └── index.eta │ ├── favicon │ │ ├── favicon.svg │ │ └── index.vto │ ├── feed │ │ ├── 404.md │ │ ├── page1.md │ │ ├── page5.yaml │ │ └── static.yml │ ├── fff │ │ ├── date.md │ │ ├── image.md │ │ └── tags.md │ ├── frontmatter-only-comment.md │ ├── google-fonts │ │ └── styles.css │ ├── icons │ │ └── index.vto │ ├── import_map.json │ ├── inline │ │ ├── favicon-with-class.svg │ │ ├── favicon.svg │ │ ├── index.vto │ │ ├── other favicon.png │ │ ├── script.js │ │ └── styles.vto │ ├── json │ │ ├── _data.jsonc │ │ ├── _includes │ │ │ └── layout.vto │ │ ├── index.page.json │ │ └── page1.md │ ├── json_ld │ │ ├── page-1.md │ │ ├── page-2.vto │ │ └── page-3.page.ts │ ├── jsx │ │ ├── _components │ │ │ └── button.jsx │ │ ├── _includes │ │ │ ├── hello.jsx │ │ │ └── layout.jsx │ │ ├── index.page.jsx │ │ ├── multiple.page.jsx │ │ ├── with-function.page.jsx │ │ └── with-markdown.md │ ├── jsx_preact │ │ ├── _components │ │ │ └── button.jsx │ │ ├── _includes │ │ │ ├── hello.jsx │ │ │ └── layout.jsx │ │ ├── index.page.jsx │ │ ├── multiple.page.jsx │ │ ├── with-comp.page.njk │ │ ├── with-function.page.jsx │ │ └── with-markdown.md │ ├── katex │ │ └── index.md │ ├── layouts │ │ ├── _includes │ │ │ ├── layout-2.vto │ │ │ ├── layout-3.vto │ │ │ ├── page.vto │ │ │ └── style.vto │ │ ├── index.md │ │ ├── page.md │ │ ├── styles.2.css │ │ └── styles.css │ ├── lightningcss │ │ ├── _includes │ │ │ └── variables.css │ │ ├── index.css │ │ └── text.css │ ├── markdown │ │ ├── basic.md │ │ ├── empty.md │ │ ├── footnote.md │ │ ├── with-attributes.md │ │ ├── with-code.md │ │ ├── with-deflist.md │ │ ├── with-filter.vto │ │ ├── with-module.page.js │ │ └── with-vento.md │ ├── mdx │ │ ├── _components │ │ │ └── Header.jsx │ │ ├── _includes │ │ │ └── Image.tsx │ │ ├── index.mdx │ │ └── mdx-filter.page.ts │ ├── metas │ │ ├── _data.js │ │ ├── page-1.vto │ │ ├── page-2.vto │ │ └── page-3.md │ ├── minify_html │ │ └── index.vto │ ├── module │ │ ├── _includes │ │ │ └── layout.js │ │ ├── multiple-async.page.js │ │ ├── multiple.page.js │ │ └── simple.page.js │ ├── multilanguage │ │ ├── _data.json │ │ ├── _includes │ │ │ └── layout.vto │ │ ├── index.md │ │ ├── lang-selector.md │ │ ├── other.vto │ │ ├── pages │ │ │ ├── page1.md │ │ │ ├── page1_en.md │ │ │ └── page1_gl.md │ │ ├── pagination.page.js │ │ └── types │ │ │ ├── article.md │ │ │ ├── article_gl.md │ │ │ ├── post.md │ │ │ └── post_gl.md │ ├── nav │ │ ├── _data.yml │ │ ├── _includes │ │ │ ├── main.vto │ │ │ └── step.vto │ │ ├── docs.md │ │ ├── docs │ │ │ ├── 0-pages │ │ │ │ ├── 1-first.md │ │ │ │ ├── 2-second.md │ │ │ │ ├── 3-pages.md │ │ │ │ ├── 3-pages │ │ │ │ │ ├── 1.first.md │ │ │ │ │ └── 2.second.md │ │ │ │ ├── 4-pages │ │ │ │ │ └── 1-pages │ │ │ │ │ │ └── 1-first.md │ │ │ │ └── index.md │ │ │ └── 1-about-docs.md │ │ └── index.vto │ ├── normal │ │ ├── 404.md │ │ ├── _data.yml │ │ ├── favicon.png │ │ ├── images │ │ │ └── avatar.jpg │ │ ├── page5.yaml │ │ ├── pages │ │ │ ├── 2020-06-21_page2.page.json │ │ │ ├── 2021-01-02-18-32_page4.page.ts │ │ │ ├── _data.yml │ │ │ ├── _data │ │ │ │ ├── colors.yml │ │ │ │ ├── documents.ts │ │ │ │ ├── drinks.js │ │ │ │ └── names.json │ │ │ ├── ghost │ │ │ │ ├── 2021-12-29-page6.md │ │ │ │ └── _data.yml │ │ │ ├── page1.md │ │ │ ├── page3.page.js │ │ │ └── subpage │ │ │ │ ├── _data.yml │ │ │ │ └── page7.page.js │ │ ├── static.yml │ │ └── styles.css │ ├── nunjucks │ │ ├── _components │ │ │ ├── Button.ts │ │ │ └── icon │ │ │ │ └── User.njk │ │ ├── _data.yml │ │ ├── _includes │ │ │ ├── basic.njk │ │ │ └── partial.njk │ │ ├── components.njk │ │ ├── data.njk │ │ ├── empty.njk │ │ ├── index.njk │ │ ├── njk-filter.page.js │ │ └── with-helpers.njk │ ├── og_images │ │ ├── _data.yml │ │ ├── _includes │ │ │ └── og_template.jsx │ │ └── page1.md │ ├── pagefind │ │ ├── _includes │ │ │ └── main.vto │ │ ├── index.md │ │ └── page2.md │ ├── picture │ │ ├── index.vto │ │ └── kevin schmid unsplash.jpg │ ├── plaintext │ │ └── index.vto │ ├── postcss │ │ ├── _includes │ │ │ ├── components │ │ │ │ └── foo.css │ │ │ └── variables.css │ │ ├── index.min.css │ │ └── text.css │ ├── prism │ │ └── index.md │ ├── pug │ │ ├── _includes │ │ │ ├── layout.pug │ │ │ └── layout2.pug │ │ ├── extends.pug │ │ ├── filter.pug │ │ └── layout.pug │ ├── purgecss │ │ ├── _includes │ │ │ ├── footer.vto │ │ │ └── layout.vto │ │ ├── index.vto │ │ ├── pages │ │ │ ├── page1.md │ │ │ └── page2.page.js │ │ ├── script.js │ │ ├── static │ │ │ └── static.html │ │ └── styles.css │ ├── reading_info │ │ ├── _includes │ │ │ └── layout.vto │ │ └── page.md │ ├── redirects │ │ ├── page1.vto │ │ └── page2.vto │ ├── relations │ │ ├── _includes │ │ │ ├── categories.vto │ │ │ ├── comment.vto │ │ │ └── posts.vto │ │ ├── categories │ │ │ ├── _data.yml │ │ │ ├── category-1.md │ │ │ └── category-2.md │ │ ├── comments │ │ │ ├── _data.yml │ │ │ ├── comment-1.md │ │ │ ├── comment-2.md │ │ │ └── comment-3.md │ │ └── posts │ │ │ ├── _data.yml │ │ │ ├── post-1.md │ │ │ └── post-2.md │ ├── relative_urls │ │ ├── about-us │ │ │ ├── contact.md │ │ │ ├── index.md │ │ │ └── presentation.md │ │ ├── index.md │ │ └── styles.css │ ├── remark │ │ ├── basic.md │ │ ├── empty.md │ │ ├── with-attributes.md │ │ ├── with-code.md │ │ ├── with-deflist.md │ │ ├── with-filter.vto │ │ ├── with-module.page.js │ │ └── with-vto.md │ ├── remote_files │ │ ├── _includes │ │ │ ├── name.js │ │ │ └── templates │ │ │ │ └── local1.njk │ │ ├── _remotes │ │ │ ├── _data.yml │ │ │ ├── asset.txt │ │ │ ├── hello.js │ │ │ ├── other-remote-style.css │ │ │ ├── remote-style.css │ │ │ ├── remote-template2.njk │ │ │ ├── remote1.njk │ │ │ ├── styles2.css │ │ │ └── variables.scss │ │ ├── local1.md │ │ ├── scripts.js │ │ ├── styles1.css │ │ └── styles3.scss │ ├── render_order │ │ ├── _includes │ │ │ └── paginate.js │ │ ├── extra-page.md │ │ ├── pages.page.ts │ │ ├── pages2.page.ts │ │ └── pagination.page.ts │ ├── resolve_urls │ │ ├── articles │ │ │ ├── article-1.md │ │ │ ├── article-2.md │ │ │ └── article-3.md │ │ ├── index.md │ │ ├── other.md │ │ ├── statics │ │ │ ├── asset.md │ │ │ └── robots.txt │ │ └── tílde-and-eñe.md │ ├── sass │ │ ├── _includes │ │ │ └── sass │ │ │ │ ├── _list.scss │ │ │ │ ├── bootstrap.scss │ │ │ │ ├── button │ │ │ │ └── index.scss │ │ │ │ ├── colors.scss │ │ │ │ └── third.scss │ │ ├── index.scss │ │ ├── index2.sass │ │ └── styles.css │ ├── search │ │ ├── a.md │ │ ├── b.md │ │ ├── c.md │ │ ├── d.md │ │ └── index.vto │ ├── sheets │ │ ├── _data │ │ │ └── attendance_numbers.numbers │ │ └── index.vto │ ├── simple │ │ ├── .page1.md │ │ ├── _page1.md │ │ ├── foo.md │ │ └── page1.md │ ├── slugify_urls │ │ ├── Chourizos ao viño.md │ │ ├── Page 1.md │ │ ├── UI Styles.css │ │ ├── _headers │ │ ├── generator.page.js │ │ ├── page-3.md │ │ └── page-4.md │ ├── sri │ │ ├── index.vto │ │ └── script.js │ ├── static_files │ │ ├── _headers │ │ ├── _static │ │ │ └── inner │ │ │ │ └── yes.txt │ │ ├── four.no │ │ ├── one.yes │ │ ├── other │ │ │ ├── one │ │ │ └── two │ │ ├── other2 │ │ │ ├── one │ │ │ └── two │ │ ├── posts │ │ │ └── 2022-01-01_first-post │ │ │ │ ├── assets │ │ │ │ ├── inner │ │ │ │ │ ├── inner2 │ │ │ │ │ │ └── styles.scss │ │ │ │ │ └── styles1.scss │ │ │ │ └── styles.scss │ │ │ │ ├── callback.copy2 │ │ │ │ ├── individual-file │ │ │ │ ├── inner │ │ │ │ ├── callback1.copy2 │ │ │ │ ├── inner2 │ │ │ │ │ ├── callback2.copy2 │ │ │ │ │ └── to-copy2.copy │ │ │ │ └── to-copy1.copy │ │ │ │ └── to-copy.copy │ │ ├── script │ │ │ └── 2022-01-01_app │ │ │ │ └── main.js │ │ ├── static │ │ │ ├── _no-copy │ │ │ ├── _redirects │ │ │ ├── one.yes │ │ │ └── two.yes │ │ ├── styles.css │ │ ├── styles │ │ │ └── main.css │ │ ├── three.no │ │ └── two.yes │ ├── svgo │ │ └── favicon.svg │ ├── symlinks │ │ ├── footer_link.njk │ │ ├── footer_link_link │ │ ├── includes_link │ │ │ ├── footer.njk │ │ │ └── layouts │ │ │ │ └── layout.njk │ │ ├── index_link.md │ │ ├── layout_link.njk │ │ └── src │ │ │ ├── _includes │ │ │ └── index.md │ ├── tailwindcss │ │ ├── index.vto │ │ ├── script.js │ │ └── styles.css │ ├── terser │ │ ├── main.js │ │ └── numbers.vto │ ├── toml │ │ ├── _data.toml │ │ ├── _includes │ │ │ └── layout.vto │ │ ├── index.page.toml │ │ └── page1.md │ ├── transform_images │ │ ├── _data.yml │ │ └── lume.PNG │ ├── unocss │ │ ├── index.vto │ │ └── styles.css │ ├── url │ │ ├── default-filter.page.js │ │ └── renamed-filter.page.js │ ├── vento │ │ ├── _components │ │ │ ├── Button.vto │ │ │ └── container.vto │ │ ├── _includes │ │ │ ├── footer.vto │ │ │ └── layout.vto │ │ ├── index.vto │ │ └── vto-filter.page.js │ └── well_known │ │ └── .well-known │ │ └── page.vto ├── base_path.test.ts ├── brotli.test.ts ├── build.test.ts ├── cache_busting.test.ts ├── check_urls.test.ts ├── classname.test.ts ├── code_highlight.test.ts ├── components.test.ts ├── config.test.ts ├── core │ ├── engines.test.ts │ ├── events.test.ts │ ├── formats.test.ts │ ├── loaders_assets │ │ ├── 1_page.txt │ │ ├── 2021-12-19-20-35_page.txt │ │ ├── 2021-12-19_page.txt │ │ ├── _data │ │ │ ├── value1.json │ │ │ ├── value2.yml │ │ │ └── value3 │ │ │ │ └── subvalue.ts │ │ ├── data.json │ │ ├── data.ts │ │ ├── data.txt │ │ ├── data.yml │ │ └── page.txt │ ├── processors.test.ts │ ├── renderer.test.ts │ ├── scopes.test.ts │ ├── scripts.test.ts │ └── utils │ │ └── data_values.test.ts ├── date.test.ts ├── decap_cms.test.ts ├── esbuild.test.ts ├── eta.test.ts ├── events.test.ts ├── extract_date.test.ts ├── favicon.test.ts ├── feed.test.ts ├── fff.test.ts ├── filter_pages.test.ts ├── google_fonts.test.ts ├── gzip.test.ts ├── icons.test.ts ├── inline.test.ts ├── json.test.ts ├── json_ld.test.ts ├── jsx.test.ts ├── katex.test.ts ├── layout.test.ts ├── lightningcss.test.ts ├── lint.test.ts ├── loaders.test.ts ├── markdown.test.ts ├── mdx.test.ts ├── metas.test.ts ├── minify_html.test.ts ├── module.test.ts ├── multilanguage.test.ts ├── nav.test.ts ├── no_cache.test.ts ├── no_cors.test.ts ├── nunjucks.test.ts ├── og_images.test.ts ├── pagefind.test.ts ├── pagination.test.ts ├── picture.test.ts ├── plaintext.test.ts ├── plugins.test.ts ├── postcss.test.ts ├── pretty_urls.test.ts ├── prism.test.ts ├── pug.test.ts ├── purgecss.test.ts ├── reading_info.test.ts ├── redirects.test.ts ├── relations.test.ts ├── relative_urls.test.ts ├── remark.test.ts ├── remote_files.test.ts ├── render_order.test.ts ├── resolve_urls.test.ts ├── robots.test.ts ├── router.test.ts ├── sass.test.ts ├── search.test.ts ├── sheets.test.ts ├── sitemap.test.ts ├── slugify_urls.test.ts ├── source_maps.test.ts ├── sri.test.ts ├── static_files.test.ts ├── svgo.test.ts ├── symlinks.test.ts ├── tailwindcss.test.ts ├── terser.test.ts ├── toml.test.ts ├── transform_images.test.ts ├── unocss.test.ts ├── url.test.ts ├── utils.test.ts ├── utils.ts ├── vento.test.ts ├── well_known.test.ts └── www.test.ts └── types.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: lume 2 | github: oscarotero 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature Request Lume Framework 2 | description: I have a suggestion and may want to implement it 😎 ! 3 | labels: [enhancement] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Do you have an idea to improve Lume? That's cool! 9 | Before submitting your idea, please search existing issues to confirm if there is already 10 | something related to your idea. 11 | - type: textarea 12 | attributes: 13 | label: "Enter your suggestions in details:" 14 | placeholder: | 15 | Your great idea! 16 | validations: 17 | required: true 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | 7 | ## Related Issues 8 | 9 | 13 | 14 | ### Check List 15 | 16 | - [ ] Have you read the 17 | [CODE OF CONDUCT](https://github.com/lumeland/lume/blob/master/CODE_OF_CONDUCT.md) 18 | - [ ] Have you read the document 19 | [CONTRIBUTING](https://github.com/lumeland/lume/blob/master/CONTRIBUTING.md) 20 | - [ ] One pull request per feature. If you want to do more than one thing, 21 | send multiple pull request. 22 | - [ ] Write tests. 23 | - [ ] Run deno `fmt` to fix the code format before commit. 24 | - [ ] Document any change in the `CHANGELOG.md`. 25 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | release: 10 | name: Create Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v3 15 | 16 | - name: Create Release 17 | uses: actions/create-release@v1 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | tag_name: ${{ github.ref }} 22 | release_name: ${{ github.ref }} 23 | body: | 24 | See the [changelog](CHANGELOG.md). 25 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Run tests 8 | strategy: 9 | matrix: 10 | deno_version: 11 | - 2.1 # Latest LTS 12 | - latest 13 | os: 14 | - ubuntu-latest 15 | - macos-latest # macos-14 16 | - macos-15 17 | - windows-latest 18 | runs-on: ${{ matrix.os }} 19 | 20 | steps: 21 | - name: Checkout code 22 | uses: actions/checkout@v4 23 | 24 | - name: Setup Deno environment 25 | uses: denoland/setup-deno@v2 26 | with: 27 | deno-version: ${{ matrix.deno_version }} 28 | 29 | - name: Verify formatting 30 | if: matrix.os == 'ubuntu-latest' 31 | run: deno fmt --check 32 | 33 | - name: Run linter 34 | run: deno lint 35 | 36 | - name: Run tests 37 | if: matrix.deno_version == 'latest' 38 | run: deno task test 39 | 40 | # Lint plugins are not supported by Deno 2.1 41 | - name: Run tests 42 | if: matrix.deno_version == 2.1 43 | run: deno task test --ignore=tests/lint.test.ts 44 | 45 | - name: Test upgrade 46 | run: deno run -A cli.ts upgrade 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tests/coverage 2 | _binary 3 | TODO 4 | deno.lock 5 | _cache -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.inlayHints.enabled": "off", 3 | "deno.enable": true, 4 | "deno.unstable": true, 5 | "deno.config": "./deno.json", 6 | "deno.suggest.imports.hosts": { 7 | "https://deno.land": true 8 | }, 9 | "workbench.startupEditor": "readme" 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 - Oscar Otero 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 | -------------------------------------------------------------------------------- /cli/build.ts: -------------------------------------------------------------------------------- 1 | import { buildSite } from "./utils.ts"; 2 | 3 | /** Build the website and optionally watch changes and serve the site */ 4 | export function build( 5 | config: string | undefined, 6 | serve?: boolean, 7 | watch?: boolean, 8 | ) { 9 | if (!serve && !watch) { 10 | buildSite(config); 11 | return; 12 | } 13 | 14 | const workerUrl = import.meta.resolve("./build_worker.ts"); 15 | let worker: Worker; 16 | 17 | function init() { 18 | let type = "build"; 19 | 20 | if (worker) { 21 | type = "rebuild"; 22 | worker.terminate(); 23 | } 24 | 25 | worker = new Worker(workerUrl, { type: "module" }); 26 | worker.postMessage({ 27 | type: "localStorage", 28 | data: { ...localStorage }, 29 | }); 30 | 31 | worker.postMessage({ 32 | type, 33 | config, 34 | serve, 35 | }); 36 | 37 | worker.onmessage = (event) => { 38 | switch (event.data.type) { 39 | case "reload": 40 | init(); 41 | break; 42 | 43 | case "localStorage": { 44 | const { method, args } = event.data; 45 | localStorage[method](...args); 46 | break; 47 | } 48 | } 49 | }; 50 | } 51 | 52 | init(); 53 | } 54 | -------------------------------------------------------------------------------- /cli/cms.ts: -------------------------------------------------------------------------------- 1 | export function runCms( 2 | config: string | undefined, 3 | ) { 4 | const workerUrl = import.meta.resolve("./cms_worker.ts"); 5 | let worker: Worker; 6 | 7 | function init() { 8 | let type = "build"; 9 | 10 | if (worker) { 11 | type = "rebuild"; 12 | worker.terminate(); 13 | } 14 | 15 | worker = new Worker(workerUrl, { type: "module" }); 16 | worker.postMessage({ 17 | type: "localStorage", 18 | data: { ...localStorage }, 19 | }); 20 | 21 | worker.postMessage({ 22 | type, 23 | config, 24 | }); 25 | 26 | worker.onmessage = (event) => { 27 | switch (event.data.type) { 28 | case "reload": 29 | init(); 30 | break; 31 | 32 | case "localStorage": { 33 | const { method, args } = event.data; 34 | localStorage[method](...args); 35 | break; 36 | } 37 | } 38 | }; 39 | } 40 | 41 | init(); 42 | } 43 | -------------------------------------------------------------------------------- /cli/run.ts: -------------------------------------------------------------------------------- 1 | import { createSite } from "./utils.ts"; 2 | 3 | /** Run one or more custom scripts */ 4 | export async function run( 5 | config: string | undefined, 6 | scripts: string[], 7 | ) { 8 | const site = await createSite(config); 9 | 10 | for (const script of scripts) { 11 | const success = await site.run(script); 12 | 13 | if (!success) { 14 | addEventListener("unload", () => Deno.exit(1)); 15 | break; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cli/upgrade.ts: -------------------------------------------------------------------------------- 1 | import { upgrade } from "../deps/init.ts"; 2 | 3 | export default async function (dev?: boolean, version?: string) { 4 | const process = upgrade({ path: ".", dev, version }); 5 | await process.run(); 6 | } 7 | -------------------------------------------------------------------------------- /core/loaders/binary.ts: -------------------------------------------------------------------------------- 1 | import { read } from "../utils/read.ts"; 2 | 3 | import type { RawData } from "../file.ts"; 4 | 5 | /** Load binary files, like images, audio, video, etc. */ 6 | export default async function binary(path: string): Promise { 7 | const content = await read(path, true); 8 | return { content }; 9 | } 10 | -------------------------------------------------------------------------------- /core/loaders/json.ts: -------------------------------------------------------------------------------- 1 | import { parse } from "../../deps/jsonc.ts"; 2 | import { isPlainObject } from "../utils/object.ts"; 3 | import { read } from "../utils/read.ts"; 4 | 5 | import type { RawData } from "../file.ts"; 6 | 7 | /** Load and parse a JSON / JSONC file */ 8 | export default async function json(path: string): Promise { 9 | const text = await read(path, false); 10 | 11 | if (!text.trim()) { 12 | return {}; 13 | } 14 | 15 | const content = path.endsWith(".jsonc") ? parse(text) : JSON.parse(text); 16 | 17 | if (!content) { 18 | return {}; 19 | } 20 | 21 | if (isPlainObject(content)) { 22 | return content as RawData; 23 | } 24 | 25 | return { content }; 26 | } 27 | -------------------------------------------------------------------------------- /core/loaders/module.ts: -------------------------------------------------------------------------------- 1 | import { isUrl } from "../utils/path.ts"; 2 | import { isPlainObject } from "../utils/object.ts"; 3 | import { env } from "../utils/env.ts"; 4 | 5 | import type { RawData } from "../file.ts"; 6 | 7 | /** Load a JavaScript/TypeScript file. Use a random hash to prevent caching */ 8 | export default async function module(path: string): Promise { 9 | const url = isUrl(path) ? path : `file://${path}`; 10 | const specifier = env("LUME_LIVE_RELOAD") 11 | ? `${url}#${Date.now()}` 12 | : url; 13 | 14 | const mod = await import(specifier); 15 | return toData(mod); 16 | } 17 | 18 | /** Transform the imported module to RawData */ 19 | export function toData(mod: Record): RawData { 20 | const data: RawData = {}; 21 | 22 | for (const [name, value] of Object.entries(mod)) { 23 | if (name === "default") { 24 | if (isPlainObject(value)) { 25 | Object.assign(data, value); 26 | } else { 27 | data.content = value; 28 | } 29 | continue; 30 | } 31 | 32 | data[name] = value; 33 | } 34 | 35 | return data; 36 | } 37 | -------------------------------------------------------------------------------- /core/loaders/text.ts: -------------------------------------------------------------------------------- 1 | import { extract, test } from "../../deps/front_matter.ts"; 2 | import { read } from "../utils/read.ts"; 3 | 4 | import type { RawData } from "../file.ts"; 5 | 6 | /** Load a text file. Detect and parse the front matter */ 7 | export default async function text(path: string): Promise { 8 | const content = await read(path, false); 9 | 10 | if (test(content)) { 11 | let { attrs, body } = extract(content); 12 | attrs ??= {}; 13 | attrs.content = body; 14 | 15 | return attrs; 16 | } 17 | 18 | return { content }; 19 | } 20 | -------------------------------------------------------------------------------- /core/loaders/toml.ts: -------------------------------------------------------------------------------- 1 | import { parse } from "../../deps/toml.ts"; 2 | import { isPlainObject } from "../utils/object.ts"; 3 | import { read } from "../utils/read.ts"; 4 | 5 | import type { RawData } from "../file.ts"; 6 | 7 | /** Load and parse a TOML file */ 8 | export default async function toml(path: string): Promise { 9 | const text = await read(path, false); 10 | const content = parse(text); 11 | 12 | if (!content) { 13 | return {}; 14 | } 15 | 16 | if (isPlainObject(content)) { 17 | return content as RawData; 18 | } 19 | 20 | return { content }; 21 | } 22 | -------------------------------------------------------------------------------- /core/loaders/yaml.ts: -------------------------------------------------------------------------------- 1 | import { parse } from "../../deps/yaml.ts"; 2 | import { isPlainObject } from "../utils/object.ts"; 3 | import { read } from "../utils/read.ts"; 4 | 5 | import type { RawData } from "../file.ts"; 6 | 7 | /** Load and parse a YAML file */ 8 | export default async function yaml(path: string): Promise { 9 | const text = await read(path, false); 10 | const content = parse(text); 11 | 12 | if (!content) { 13 | return {}; 14 | } 15 | 16 | if (isPlainObject(content)) { 17 | return content as RawData; 18 | } 19 | 20 | return { content }; 21 | } 22 | -------------------------------------------------------------------------------- /core/utils/concurrent.ts: -------------------------------------------------------------------------------- 1 | /** Run a callback concurrently with all the elements of an Iterable */ 2 | export async function concurrent( 3 | iterable: AsyncIterable | Iterable, 4 | iteratorFn: (arg: Type) => Promise, 5 | limit = 200, 6 | ) { 7 | const executing: Promise[] = []; 8 | 9 | for await (const item of iterable) { 10 | const p: Promise = iteratorFn(item).then(() => 11 | executing.splice(executing.indexOf(p), 1) 12 | ); 13 | 14 | executing.push(p); 15 | 16 | if (executing.length >= limit) { 17 | await Promise.race(executing); 18 | } 19 | } 20 | 21 | await Promise.all(executing); 22 | } 23 | -------------------------------------------------------------------------------- /core/utils/digest.ts: -------------------------------------------------------------------------------- 1 | import { crypto } from "../../deps/crypto.ts"; 2 | import { encodeHex } from "../../deps/hex.ts"; 3 | 4 | const decoder = new TextDecoder(); 5 | const encoder = new TextEncoder(); 6 | 7 | /** Digest a message using SHA-1 algorithm */ 8 | export async function sha1(message: string | Uint8Array): Promise { 9 | if (typeof message === "string") { 10 | message = encoder.encode(message); 11 | } 12 | 13 | const hash = await crypto.subtle.digest("SHA-1", message); 14 | return decoder.decode(hash); 15 | } 16 | 17 | /** Digest a message using MD5 algorithm */ 18 | export async function md5(message: string | Uint8Array): Promise { 19 | if (typeof message === "string") { 20 | message = encoder.encode(message); 21 | } 22 | 23 | const hash = await crypto.subtle.digest("MD5", message); 24 | return encodeHex(hash); 25 | } 26 | -------------------------------------------------------------------------------- /core/utils/dom.ts: -------------------------------------------------------------------------------- 1 | import { DOMParser } from "../../deps/dom.ts"; 2 | 3 | const parser = new DOMParser(); 4 | 5 | /** Convert an Document instance to a string */ 6 | export function documentToString(document: Document) { 7 | const { doctype, documentElement } = document; 8 | 9 | if (!doctype) { 10 | return documentElement?.outerHTML || ""; 11 | } 12 | 13 | return `\n${documentElement?.outerHTML}`; 18 | } 19 | 20 | /** Parse a string with HTML code and return a Document */ 21 | export function stringToDocument(string: string): Document { 22 | const document = parser.parseFromString(string, "text/html"); 23 | 24 | if (!document) { 25 | throw new Error("Unable to parse the HTML code"); 26 | } 27 | 28 | return document as unknown as Document; 29 | } 30 | -------------------------------------------------------------------------------- /core/utils/dom_links.ts: -------------------------------------------------------------------------------- 1 | export interface Link { 2 | element: Element; 3 | attribute: string; 4 | value: string; 5 | } 6 | 7 | const selectors = { 8 | href: "[href]", 9 | src: "[src]", 10 | poster: "video[poster]", 11 | srcset: "[srcset]", 12 | imagesrcset: "[imagesrcset]", 13 | }; 14 | 15 | export function* searchLinks(document: Document): Iterable { 16 | for (const [attribute, selector] of Object.entries(selectors)) { 17 | for (const element of document.querySelectorAll(selector)) { 18 | yield { 19 | element, 20 | attribute, 21 | value: element.getAttribute(attribute)?.trim() ?? "", 22 | }; 23 | } 24 | } 25 | } 26 | 27 | export function parseSrcset(srcset: string): [string, string][] { 28 | return srcset.trim().split(",").map((src) => { 29 | const [, url, rest] = src.trim().match(/^(\S+)(.*)/)!; 30 | return [url, rest]; 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /core/utils/generator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if the content variable is a generator. 3 | */ 4 | export function isGenerator( 5 | content: unknown, 6 | ): content is GeneratorFunction | AsyncGeneratorFunction { 7 | if (typeof content !== "function") { 8 | return false; 9 | } 10 | 11 | const name = content.constructor.name; 12 | return (name === "GeneratorFunction" || name === "AsyncGeneratorFunction"); 13 | } 14 | -------------------------------------------------------------------------------- /core/utils/lume_version.ts: -------------------------------------------------------------------------------- 1 | /** Return the current installed version */ 2 | export function getCurrentVersion( 3 | url = new URL(import.meta.resolve("../")), 4 | ): string { 5 | const { pathname } = url; 6 | return pathname.match(/@([^/]+)/)?.[1] ?? `local (${pathname})`; 7 | } 8 | 9 | /** Return the Lume generator value (for , Feed, etc) */ 10 | export function getGenerator() { 11 | const version = getCurrentVersion(); 12 | 13 | if (version.startsWith("local")) { 14 | return "Lume"; 15 | } 16 | 17 | return `Lume ${version}`; 18 | } 19 | -------------------------------------------------------------------------------- /core/utils/net.ts: -------------------------------------------------------------------------------- 1 | export function localIp(): string | undefined { 2 | // Try/catch for https://github.com/denoland/deno/issues/25420 3 | try { 4 | for (const info of Deno.networkInterfaces()) { 5 | if (info.family !== "IPv4" || info.address.startsWith("127.")) { 6 | continue; 7 | } 8 | 9 | return info.address; 10 | } 11 | } catch { 12 | return undefined; 13 | } 14 | } 15 | 16 | export async function openBrowser(url: string): Promise { 17 | const commands: Record = { 18 | darwin: "open", 19 | linux: "xdg-open", 20 | freebsd: "xdg-open", 21 | netbsd: "xdg-open", 22 | aix: "xdg-open", 23 | solaris: "xdg-open", 24 | illumos: "xdg-open", 25 | windows: "explorer", 26 | android: "xdg-open", 27 | }; 28 | 29 | await new Deno.Command(commands[Deno.build.os], { 30 | args: [url], 31 | stdout: "inherit", 32 | stderr: "inherit", 33 | }).output(); 34 | } 35 | -------------------------------------------------------------------------------- /core/utils/page_content.ts: -------------------------------------------------------------------------------- 1 | export function insertContent( 2 | content: string, 3 | newContent: string, 4 | placeholder?: string, 5 | ) { 6 | if (content) { 7 | if (placeholder && content.includes(placeholder)) { 8 | return content.replace( 9 | placeholder, 10 | newContent, 11 | ); 12 | } 13 | return `${content}\n${newContent}`; 14 | } 15 | 16 | return newContent; 17 | } 18 | -------------------------------------------------------------------------------- /core/utils/tokens.ts: -------------------------------------------------------------------------------- 1 | import { env } from "./env.ts"; 2 | 3 | const tokens = new Map(); 4 | const denoAuthTokens = env("DENO_AUTH_TOKENS"); 5 | if (denoAuthTokens) { 6 | const authTokens = denoAuthTokens.split(";"); 7 | for (const token of authTokens) { 8 | const [credentials, host] = token.split("@"); 9 | if (credentials.includes(":")) { 10 | // Basic Auth 11 | const encodedCredentials = btoa(credentials); 12 | tokens.set(host, `Basic ${encodedCredentials}`); 13 | } else { 14 | // Bearer Token 15 | tokens.set(host, `Bearer ${credentials}`); 16 | } 17 | } 18 | } 19 | 20 | export { tokens }; 21 | -------------------------------------------------------------------------------- /deps/assert.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/assert@1.0.13"; 2 | -------------------------------------------------------------------------------- /deps/base64.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/encoding@1.0.10/base64"; 2 | -------------------------------------------------------------------------------- /deps/brotli.ts: -------------------------------------------------------------------------------- 1 | export * from "https://deno.land/x/brotli@0.1.7/mod.ts"; 2 | -------------------------------------------------------------------------------- /deps/cli.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/cli@1.0.19"; 2 | -------------------------------------------------------------------------------- /deps/cliffy.ts: -------------------------------------------------------------------------------- 1 | export * from "https://deno.land/x/cliffy@v0.25.7/mod.ts"; 2 | -------------------------------------------------------------------------------- /deps/cms.ts: -------------------------------------------------------------------------------- 1 | export { default as adapter } from "https://deno.land/x/lume_cms_adapter@v0.2.2/adapter.ts"; 2 | -------------------------------------------------------------------------------- /deps/colors.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/fmt@1.0.8/colors"; 2 | -------------------------------------------------------------------------------- /deps/crypto.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/crypto@1.0.5"; 2 | -------------------------------------------------------------------------------- /deps/date.ts: -------------------------------------------------------------------------------- 1 | export { format } from "npm:date-fns@4.1.0/format"; 2 | export { formatDistanceToNow } from "npm:date-fns@4.1.0/formatDistanceToNow"; 3 | export { formatDistanceToNowStrict } from "npm:date-fns@4.1.0/formatDistanceToNowStrict"; 4 | export type { Locale } from "npm:date-fns@4.1.0/locale"; 5 | -------------------------------------------------------------------------------- /deps/debugbar.ts: -------------------------------------------------------------------------------- 1 | export type { 2 | Action, 3 | Collection, 4 | Item, 5 | } from "https://cdn.jsdelivr.net/gh/lumeland/bar@0.1.6/types.ts"; 6 | export const specifier = 7 | "https://cdn.jsdelivr.net/gh/lumeland/bar@0.1.6/lume-bar.js"; 8 | -------------------------------------------------------------------------------- /deps/decap.ts: -------------------------------------------------------------------------------- 1 | export const decapUrl = 2 | "https://cdn.jsdelivr.net/npm/decap-cms@3.6.3/dist/decap-cms.js"; 3 | export const serverUrl = "npm:decap-server@3.2.0"; 4 | -------------------------------------------------------------------------------- /deps/dom.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | export * from "https://deno.land/x/deno_dom@v0.1.49/deno-dom-wasm.ts"; 4 | -------------------------------------------------------------------------------- /deps/esbuild.ts: -------------------------------------------------------------------------------- 1 | export * from "https://deno.land/x/esbuild@v0.25.5/mod.js"; 2 | export { denoPlugins } from "jsr:@luca/esbuild-deno-loader@0.11.1"; 3 | -------------------------------------------------------------------------------- /deps/eta.ts: -------------------------------------------------------------------------------- 1 | export { Eta } from "https://deno.land/x/eta@v3.5.0/src/index.ts"; 2 | export type { EtaConfig } from "https://deno.land/x/eta@v3.5.0/src/config.ts"; 3 | -------------------------------------------------------------------------------- /deps/fff.ts: -------------------------------------------------------------------------------- 1 | export type * from "https://deno.land/x/fff@v1.2.1/src/types.ts"; 2 | 3 | export { 4 | postTypeDiscovery, 5 | } from "https://deno.land/x/fff@v1.2.1/src/utils/ptd.ts"; 6 | 7 | export { 8 | type FFFTransformPreset, 9 | transform, 10 | } from "https://deno.land/x/fff@v1.2.1/src/utils/transform.ts"; 11 | 12 | export { 13 | strict, 14 | type StrictPresetOptions, 15 | } from "https://deno.land/x/fff@v1.2.1/src/utils/presets/strict.ts"; 16 | -------------------------------------------------------------------------------- /deps/front_matter.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/front-matter@1.0.9/any"; 2 | export { test } from "jsr:@std/front-matter@1.0.9"; 3 | -------------------------------------------------------------------------------- /deps/fs.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/fs@1.0.18"; 2 | -------------------------------------------------------------------------------- /deps/hex.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/encoding@1.0.10/hex"; 2 | -------------------------------------------------------------------------------- /deps/highlight.ts: -------------------------------------------------------------------------------- 1 | import hljs from "npm:highlight.js@11.11.1"; 2 | export * from "npm:highlight.js@11.11.1"; 3 | export default hljs; 4 | 5 | export const themesPath = 6 | "https://cdn.jsdelivr.net/npm/highlight.js@11.11.1/styles/"; 7 | -------------------------------------------------------------------------------- /deps/http.ts: -------------------------------------------------------------------------------- 1 | export { serveFile } from "jsr:@std/http@1.0.17/file-server"; 2 | -------------------------------------------------------------------------------- /deps/init.ts: -------------------------------------------------------------------------------- 1 | const res = await fetch( 2 | `https://cdn.deno.land/lume_init/meta/versions.json`, 3 | ); 4 | const versions = await res.json(); 5 | const { run } = await import( 6 | `https://deno.land/x/lume_init@${versions.latest}/mod.ts` 7 | ); 8 | const { default: upgrade } = await import( 9 | `https://deno.land/x/lume_init@${versions.latest}/upgrade.ts` 10 | ); 11 | 12 | export { run, upgrade }; 13 | -------------------------------------------------------------------------------- /deps/jsonc.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/jsonc@1.0.2"; 2 | -------------------------------------------------------------------------------- /deps/katex-auto-render/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2020 Khan Academy and other contributors 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 | -------------------------------------------------------------------------------- /deps/katex.ts: -------------------------------------------------------------------------------- 1 | import katex, { type KatexOptions as BaseOptions } from "npm:katex@0.16.22"; 2 | 3 | export const assetsUrl = "https://cdn.jsdelivr.net/npm/katex@0.16.22/dist"; 4 | 5 | export { katex }; 6 | 7 | /** 8 | * Auto-render specific options 9 | */ 10 | 11 | interface Delimiter { 12 | left: string; 13 | right: string; 14 | display?: boolean | undefined; 15 | } 16 | 17 | export interface KatexOptions extends BaseOptions { 18 | delimiters?: Delimiter[] | undefined; 19 | ignoredTags?: string[] | undefined; 20 | ignoredClasses?: string[] | undefined; 21 | preProcess?: ((math: string) => string) | undefined; 22 | } 23 | -------------------------------------------------------------------------------- /deps/lightningcss.ts: -------------------------------------------------------------------------------- 1 | export * from "npm:lightningcss-wasm@1.30.1"; 2 | -------------------------------------------------------------------------------- /deps/mdx.ts: -------------------------------------------------------------------------------- 1 | export * from "npm:@mdx-js/mdx@3.1.0"; 2 | export { default as remarkGfm } from "npm:remark-gfm@4.0.1"; 3 | -------------------------------------------------------------------------------- /deps/media_types.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/media-types@1.1.0"; 2 | -------------------------------------------------------------------------------- /deps/nunjucks.ts: -------------------------------------------------------------------------------- 1 | // @deno-types="npm:@types/nunjucks@3.2.6" 2 | export { default } from "npm:nunjucks@3.2.4"; 3 | -------------------------------------------------------------------------------- /deps/outdent.ts: -------------------------------------------------------------------------------- 1 | export * from "https://deno.land/x/outdent@v0.8.0/mod.ts"; 2 | -------------------------------------------------------------------------------- /deps/pagefind.ts: -------------------------------------------------------------------------------- 1 | export * as pagefind from "npm:pagefind@1.3.0"; 2 | export type { CustomRecord } from "npm:pagefind@1.3.0"; 3 | 4 | export interface TranslationsOptions { 5 | /** English default: "Search" */ 6 | placeholder?: string; 7 | /** English default: "Clear" */ 8 | clear_search?: string; 9 | /** English default: "Load more results" */ 10 | load_more?: string; 11 | /** English default: "Search this site" */ 12 | search_label?: string; 13 | /** English default: "Filters" */ 14 | filters_label?: string; 15 | /** English default: "No results for [SEARCH_TERM]" */ 16 | zero_results?: string; 17 | /** English default: "[COUNT] results for [SEARCH_TERM]" */ 18 | many_results?: string; 19 | /** English default: "[COUNT] result for [SEARCH_TERM]" */ 20 | one_result?: string; 21 | /** English default: "No results for [SEARCH_TERM]. Showing results for [DIFFERENT_TERM] instead" */ 22 | alt_search?: string; 23 | /** English default: "No results for [SEARCH_TERM]. Try one of the following searches:" */ 24 | search_suggestion?: string; 25 | /** English default: "Searching for [SEARCH_TERM]..." */ 26 | searching?: string; 27 | } 28 | -------------------------------------------------------------------------------- /deps/path.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/path@1.1.0"; 2 | export * as posix from "jsr:@std/path@1.1.0/posix"; 3 | -------------------------------------------------------------------------------- /deps/postcss.ts: -------------------------------------------------------------------------------- 1 | export { default as postcss } from "npm:postcss@8.5.4"; 2 | export { default as postcssImport } from "npm:postcss-import@16.1.0"; 3 | export { default as autoprefixer } from "npm:autoprefixer@10.4.21"; 4 | -------------------------------------------------------------------------------- /deps/prism.ts: -------------------------------------------------------------------------------- 1 | export { default } from "npm:prismjs@1.30.0"; 2 | 3 | export const themesPath = "https://cdn.jsdelivr.net/npm/prismjs@1.30.0/themes/"; 4 | -------------------------------------------------------------------------------- /deps/pug.ts: -------------------------------------------------------------------------------- 1 | // @deno-types="npm:@types/pug@2.0.10" 2 | export * from "npm:pug@3.0.3"; 3 | -------------------------------------------------------------------------------- /deps/purgecss.ts: -------------------------------------------------------------------------------- 1 | export * from "npm:purgecss@7.0.2"; 2 | export { default as purgeHtml } from "npm:purgecss-from-html@7.0.2/lib/purgecss-from-html.esm.js"; 3 | -------------------------------------------------------------------------------- /deps/remark.ts: -------------------------------------------------------------------------------- 1 | export { default as rehypeRaw } from "npm:rehype-raw@7.0.0"; 2 | export { default as rehypeSanitize } from "npm:rehype-sanitize@6.0.0"; 3 | export { default as rehypeStringify } from "npm:rehype-stringify@10.0.1"; 4 | export { default as remarkGfm } from "npm:remark-gfm@4.0.1"; 5 | export { default as remarkParse } from "npm:remark-parse@11.0.0"; 6 | export { default as remarkRehype } from "npm:remark-rehype@11.1.2"; 7 | export * as unified from "npm:unified@11.0.5"; 8 | 9 | export type { Options as RehypeOptions } from "npm:remark-rehype@11.1.2"; 10 | export type { PluggableList } from "npm:unified@11.0.5"; 11 | -------------------------------------------------------------------------------- /deps/remove-markdown.ts: -------------------------------------------------------------------------------- 1 | import removeMarkdown from "npm:remove-markdown@0.6.2"; 2 | 3 | export function plainText(md: string, options?: RemoveMarkdownOptions): string { 4 | return removeMarkdown(md, options).replaceAll(/\s+/g, " ").trim(); 5 | } 6 | 7 | export interface RemoveMarkdownOptions { 8 | /** strip list leaders (default: true) */ 9 | stripListLeaders?: boolean; 10 | 11 | /** char to insert instead of stripped list leaders (default: '') */ 12 | listUnicodeChar?: string; 13 | 14 | /** support GitHub-Flavored Markdown (default: true) */ 15 | gfm?: boolean; 16 | 17 | /** replace images with alt-text, if present (default: true) */ 18 | useImgAltText: boolean; 19 | 20 | abbr?: boolean; 21 | 22 | /** replace links with URLs instead anchor text (default: false) */ 23 | replaceLinksWithURL?: boolean; 24 | htmlTagsToSkip?: string[]; 25 | } 26 | -------------------------------------------------------------------------------- /deps/sass.ts: -------------------------------------------------------------------------------- 1 | export * from "npm:sass@1.89.1"; 2 | -------------------------------------------------------------------------------- /deps/satori.ts: -------------------------------------------------------------------------------- 1 | export { default } from "npm:satori@0.13.2"; 2 | export * from "npm:satori@0.13.2"; 3 | export const fontsSpecifier = 4 | "https://cdn.jsdelivr.net/npm/@xz/fonts@1.0.2/serve/src"; 5 | -------------------------------------------------------------------------------- /deps/schema-dts.ts: -------------------------------------------------------------------------------- 1 | export type { Graph, Thing } from "npm:schema-dts@1.1.5"; 2 | -------------------------------------------------------------------------------- /deps/sharp.ts: -------------------------------------------------------------------------------- 1 | export { default } from "npm:sharp@0.34.2"; 2 | import sharp from "npm:sharp@0.34.2"; 3 | import icoEndec from "npm:ico-endec@0.1.6"; 4 | import { svg2png } from "./svg2png.ts"; 5 | 6 | export async function sharpsToIco(...images: sharp.Sharp[]) { 7 | const buffers = await Promise.all( 8 | images.map((image) => image.toFormat("png").toBuffer()), 9 | ); 10 | 11 | // deno-lint-ignore no-explicit-any 12 | return icoEndec.encode(buffers.map((buffer: any) => buffer.buffer)); 13 | } 14 | 15 | export async function create( 16 | content: Uint8Array | string, 17 | config: sharp.SharpOptions = {}, 18 | ): Promise { 19 | // It's a SVG 20 | if (typeof content === "string") { 21 | return sharp(await svg2png(content)); 22 | } 23 | 24 | return sharp(content, config); 25 | } 26 | -------------------------------------------------------------------------------- /deps/sheetjs.ts: -------------------------------------------------------------------------------- 1 | // @deno-types="https://cdn.sheetjs.com/xlsx-0.20.3/package/types/index.d.ts" 2 | export * from "https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs"; 3 | -------------------------------------------------------------------------------- /deps/snapshot.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/testing@1.0.13/snapshot"; 2 | -------------------------------------------------------------------------------- /deps/ssx.ts: -------------------------------------------------------------------------------- 1 | export { renderComponent } from "lume/jsx-runtime"; 2 | 3 | const ssxElement = Symbol.for("ssx.element"); 4 | export function isComponent(obj: unknown): obj is Record { 5 | // @ts-ignore: Property '[ssxElement]' does not exist on type '{}' 6 | return typeof obj === "object" && obj !== null && obj[ssxElement] === true; 7 | } 8 | -------------------------------------------------------------------------------- /deps/streams.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/streams@1.0.9"; 2 | -------------------------------------------------------------------------------- /deps/svg2png.ts: -------------------------------------------------------------------------------- 1 | import { initialize, svg2png } from "npm:svg2png-wasm@1.4.1"; 2 | import { read } from "../core/utils/read.ts"; 3 | 4 | // Initialize the WASM module 5 | const url = 6 | "https://cdn.jsdelivr.net/npm/svg2png-wasm@1.4.1/svg2png_wasm_bg.wasm"; 7 | const wasm = await read(url, true); 8 | await initialize(wasm); 9 | 10 | export { svg2png }; 11 | -------------------------------------------------------------------------------- /deps/svgo.ts: -------------------------------------------------------------------------------- 1 | export { optimize } from "npm:svgo@3.3.2"; 2 | export type { Config } from "npm:svgo@3.3.2"; 3 | -------------------------------------------------------------------------------- /deps/tailwindcss.ts: -------------------------------------------------------------------------------- 1 | export { compile } from "npm:tailwindcss@4.1.8"; 2 | export { toSourceMap } from "npm:@tailwindcss/node@4.1.8"; 3 | export { type ChangedContent, Scanner } from "npm:@tailwindcss/oxide@4.1.8"; 4 | export const specifier = "https://cdn.jsdelivr.net/npm/tailwindcss@4.1.8"; 5 | -------------------------------------------------------------------------------- /deps/terser.ts: -------------------------------------------------------------------------------- 1 | export { minify } from "npm:terser@5.41.0"; 2 | export type { MinifyOptions } from "npm:terser@5.41.0"; 3 | -------------------------------------------------------------------------------- /deps/toml.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/toml@1.0.7"; 2 | -------------------------------------------------------------------------------- /deps/unocss.ts: -------------------------------------------------------------------------------- 1 | export { 2 | createGenerator, 3 | type SourceCodeTransformer, 4 | type UnocssPluginContext, 5 | type UserConfig, 6 | } from "npm:@unocss/core@66.1.3"; 7 | 8 | export { presetWind3 } from "npm:@unocss/preset-wind3@66.1.3"; 9 | export { default as transformerVariantGroup } from "npm:@unocss/transformer-variant-group@66.1.3"; 10 | export { default as transformerDirectives } from "npm:@unocss/transformer-directives@66.1.3"; 11 | export { default as MagicString } from "npm:magic-string@0.30.17"; 12 | 13 | export const resetUrl = "https://cdn.jsdelivr.net/npm/@unocss/reset@66.1.3"; 14 | -------------------------------------------------------------------------------- /deps/vento.ts: -------------------------------------------------------------------------------- 1 | export { default as engine } from "https://deno.land/x/vento@v1.13.2/mod.ts"; 2 | export { default as autotrim } from "https://deno.land/x/vento@v1.13.2/plugins/auto_trim.ts"; 3 | 4 | export type { 5 | Environment, 6 | Plugin, 7 | } from "https://deno.land/x/vento@v1.13.2/src/environment.ts"; 8 | export type { Loader } from "https://deno.land/x/vento@v1.13.2/src/loader.ts"; 9 | export type { Token } from "https://deno.land/x/vento@v1.13.2/src/tokenizer.ts"; 10 | -------------------------------------------------------------------------------- /deps/xml.ts: -------------------------------------------------------------------------------- 1 | export * from "https://deno.land/x/xml@7.0.0/mod.ts"; 2 | -------------------------------------------------------------------------------- /deps/yaml.ts: -------------------------------------------------------------------------------- 1 | export * from "jsr:@std/yaml@1.0.7"; 2 | -------------------------------------------------------------------------------- /middlewares/cache_busting.ts: -------------------------------------------------------------------------------- 1 | import type { Middleware } from "../core/server.ts"; 2 | 3 | export interface Options { 4 | /** The regex to match the cache busting pattern */ 5 | regex: RegExp; 6 | /** The replacement string */ 7 | replacement: string; 8 | } 9 | 10 | export const defaults: Options = { 11 | regex: /^\/v[\d]+\//, 12 | replacement: "/", 13 | }; 14 | 15 | /** Implements cache busting */ 16 | export function cacheBusting(options?: Partial): Middleware { 17 | const { regex, replacement } = { ...defaults, ...options }; 18 | 19 | return async (request, next) => { 20 | const url = new URL(request.url); 21 | const { pathname } = url; 22 | 23 | if (pathname.match(regex)) { 24 | url.pathname = pathname.replace(regex, replacement); 25 | request = new Request(url.href, { 26 | ...request, 27 | }); 28 | } 29 | 30 | return await next(request); 31 | }; 32 | } 33 | 34 | export default cacheBusting; 35 | -------------------------------------------------------------------------------- /middlewares/logger.ts: -------------------------------------------------------------------------------- 1 | import { brightGreen, gray, red } from "../deps/colors.ts"; 2 | 3 | import type { Middleware } from "../core/server.ts"; 4 | 5 | /** Log the request/responses */ 6 | export function log(): Middleware { 7 | return async (request, next) => { 8 | try { 9 | const response = await next(request); 10 | const url = new URL(request.url); 11 | const pathname = decodeURIComponent(url.pathname); 12 | const { status } = response; 13 | 14 | if (status === 404 || status === 500) { 15 | console.log(`${red(status.toString())} ${pathname}`); 16 | } else if (status === 200) { 17 | console.log(`${brightGreen(status.toString())} ${pathname}`); 18 | } else if (status === 301 || status === 302) { 19 | console.log( 20 | `${gray(status.toString())} ${pathname} => ${ 21 | response.headers?.get( 22 | "location", 23 | ) 24 | }`, 25 | ); 26 | } else { 27 | console.log(`${gray(status.toString())} ${pathname}`); 28 | } 29 | 30 | return response; 31 | } catch (cause) { 32 | return new Response( 33 | `Error: ${cause.toString()}`, 34 | { status: 500 }, 35 | ); 36 | } 37 | }; 38 | } 39 | 40 | export default log; 41 | -------------------------------------------------------------------------------- /middlewares/no_cache.ts: -------------------------------------------------------------------------------- 1 | import type { Middleware } from "../core/server.ts"; 2 | 3 | /** Add a header to prevent the browser cache */ 4 | export function noCache(): Middleware { 5 | return async (request, next) => { 6 | const response = await next(request); 7 | const { headers } = response; 8 | headers.set("cache-control", "no-cache no-store must-revalidate"); 9 | headers.delete("last-modified"); 10 | headers.delete("etag"); 11 | 12 | return response; 13 | }; 14 | } 15 | 16 | export default noCache; 17 | -------------------------------------------------------------------------------- /middlewares/no_cors.ts: -------------------------------------------------------------------------------- 1 | import type { Middleware } from "../core/server.ts"; 2 | 3 | /** Add a header to prevent CORS errors (used in development) */ 4 | export function noCors(): Middleware { 5 | return async (request, next) => { 6 | const response = await next(request); 7 | response.headers.set("access-control-allow-origin", "*"); 8 | 9 | return response; 10 | }; 11 | } 12 | 13 | export default noCors; 14 | -------------------------------------------------------------------------------- /middlewares/serve_folder.ts: -------------------------------------------------------------------------------- 1 | import { serveFile } from "../core/server.ts"; 2 | 3 | import type { Middleware, RequestHandler } from "../core/server.ts"; 4 | 5 | /** The options to configure the middleware server */ 6 | export interface Options { 7 | /** The root path */ 8 | root: string; 9 | 10 | /** Serve the file as a fallback of the main middleware */ 11 | after?: boolean; 12 | } 13 | 14 | export function serveFolder(options: Options): Middleware { 15 | return async function ( 16 | request: Request, 17 | next: RequestHandler, 18 | ): Promise { 19 | let mainResponse: Response | undefined; 20 | 21 | if (options.after) { 22 | mainResponse = await next(request); 23 | 24 | if (mainResponse.status < 400) { 25 | return mainResponse; 26 | } 27 | } 28 | 29 | const altResponse = await serveFile(options.root, request); 30 | 31 | if (altResponse.status < 400) { 32 | return altResponse; 33 | } 34 | 35 | return mainResponse || next(request); 36 | }; 37 | } 38 | 39 | export default serveFolder; 40 | -------------------------------------------------------------------------------- /middlewares/shutdown.ts: -------------------------------------------------------------------------------- 1 | import type { Middleware } from "../core/server.ts"; 2 | 3 | interface Options { 4 | page: string; 5 | retryAfter: number; 6 | } 7 | 8 | const defaults: Options = { 9 | page: "/503.html", 10 | retryAfter: 60 * 60 * 24, // 24 hours 11 | }; 12 | 13 | export function shutdown( 14 | userOptions?: Partial, 15 | ): Middleware { 16 | const options = { ...defaults, ...userOptions }; 17 | 18 | return async (request, next) => { 19 | if (!isHtml(request)) { 20 | return await next(request); 21 | } 22 | 23 | const url = new URL(options.page, request.url).toString(); 24 | const response = await next(new Request(url, request)); 25 | 26 | const { body, headers } = response; 27 | headers.set("Retry-After", options.retryAfter.toString()); 28 | 29 | return new Response(body, { 30 | status: 503, 31 | statusText: "Service Unavailable", 32 | headers, 33 | }); 34 | }; 35 | } 36 | 37 | function isHtml(request: Request) { 38 | const accept = request.headers.get("accept"); 39 | return accept && accept.includes("text/html"); 40 | } 41 | 42 | export default shutdown; 43 | -------------------------------------------------------------------------------- /plugins/base_path.ts: -------------------------------------------------------------------------------- 1 | import modifyUrls from "./modify_urls.ts"; 2 | 3 | import type Site from "../core/site.ts"; 4 | 5 | /** 6 | * A plugin to prepend a base path to all internal URLs 7 | * @see https://lume.land/plugins/base_path/ 8 | */ 9 | export function basePath() { 10 | return (site: Site) => { 11 | site.use(modifyUrls({ 12 | fn: (url) => url.startsWith("/") ? site.url(url) : url, 13 | })); 14 | }; 15 | } 16 | 17 | export default basePath; 18 | -------------------------------------------------------------------------------- /plugins/filter_pages.ts: -------------------------------------------------------------------------------- 1 | import { merge } from "../core/utils/object.ts"; 2 | 3 | import type Site from "../core/site.ts"; 4 | import type { Page } from "../core/file.ts"; 5 | 6 | export interface Options { 7 | /** 8 | * The function to test the page 9 | * @default `(page) => true` 10 | */ 11 | fn: (page: Page) => boolean; 12 | } 13 | 14 | // Default options 15 | export const defaults: Options = { 16 | fn: () => true, 17 | }; 18 | 19 | /** 20 | * A plugin to filter pages 21 | * @see https://lume.land/plugins/filter_pages/ 22 | */ 23 | export function filterPages(userOptions: Options) { 24 | const options = merge(defaults, userOptions); 25 | 26 | return (site: Site) => { 27 | site.process((pages, allPages) => { 28 | for (const page of pages) { 29 | if (!options.fn(page)) { 30 | allPages.splice(allPages.indexOf(page), 1); 31 | } 32 | } 33 | }); 34 | }; 35 | } 36 | 37 | export default filterPages; 38 | -------------------------------------------------------------------------------- /plugins/json.ts: -------------------------------------------------------------------------------- 1 | import jsonLoader from "../core/loaders/json.ts"; 2 | import { merge } from "../core/utils/object.ts"; 3 | 4 | import type Site from "../core/site.ts"; 5 | 6 | export interface Options { 7 | /** File extensions to load */ 8 | extensions?: string[]; 9 | 10 | /** Optional sub-extension for page files */ 11 | pageSubExtension?: string; 12 | } 13 | 14 | // Default options 15 | export const defaults: Options = { 16 | extensions: [".json", ".jsonc"], 17 | pageSubExtension: ".page", 18 | }; 19 | 20 | /** 21 | * A plugin to load JSON files as data and pages 22 | * Installed by default 23 | * @see https://lume.land/plugins/json/ 24 | */ 25 | export function json(userOptions?: Options) { 26 | const options = merge(defaults, userOptions); 27 | 28 | return (site: Site) => { 29 | site.loadData(options.extensions, jsonLoader); 30 | site.loadPages(options.extensions, { 31 | pageSubExtension: options.pageSubExtension, 32 | loader: jsonLoader, 33 | }); 34 | }; 35 | } 36 | 37 | export default json; 38 | -------------------------------------------------------------------------------- /plugins/plaintext.ts: -------------------------------------------------------------------------------- 1 | import { plainText } from "../deps/remove-markdown.ts"; 2 | 3 | import type { RemoveMarkdownOptions } from "../deps/remove-markdown.ts"; 4 | import type Site from "../core/site.ts"; 5 | 6 | export interface Options extends RemoveMarkdownOptions { 7 | } 8 | 9 | export const defaults: Options = { 10 | stripListLeaders: true, 11 | gfm: true, 12 | useImgAltText: true, 13 | replaceLinksWithURL: false, 14 | }; 15 | 16 | /** 17 | * Plugin to convert a markdown or HTML string to plain text 18 | * @see https://lume.land/plugins/plaintext/ 19 | */ 20 | export function plaintext(userOptions?: Options) { 21 | const options = { ...defaults, ...userOptions }; 22 | 23 | return (site: Site) => { 24 | site.filter("plaintext", (text?: unknown, opts?: Options) => { 25 | if (typeof text === "string") { 26 | return plainText(text, opts || options); 27 | } 28 | 29 | return text; 30 | }); 31 | }; 32 | } 33 | 34 | export default plaintext; 35 | 36 | /** Extends Helpers interface */ 37 | declare global { 38 | namespace Lume { 39 | export interface Helpers { 40 | /** @see https://lume.land/plugins/plaintext/ */ 41 | plaintext: (text?: T, options?: Options) => T; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /plugins/relative_urls.ts: -------------------------------------------------------------------------------- 1 | import { posix } from "../deps/path.ts"; 2 | import modifyUrls from "./modify_urls.ts"; 3 | 4 | import type Site from "../core/site.ts"; 5 | 6 | /** 7 | * A plugin to convert all internal URLs to relative 8 | * @see https://lume.land/plugins/relative_urls/ 9 | */ 10 | export function relativeUrls() { 11 | return (site: Site) => { 12 | const basePath = site.options.location.pathname; 13 | 14 | site.use(modifyUrls({ 15 | fn(url, page) { 16 | if (!url.startsWith("/") || url.startsWith("//")) { 17 | return url; 18 | } 19 | 20 | if (!url.startsWith(basePath)) { 21 | url = posix.join(basePath, url); 22 | } 23 | 24 | const from = site.url(page.outputPath); 25 | return posix.join(".", posix.relative(posix.dirname(from), url)); 26 | }, 27 | })); 28 | }; 29 | } 30 | 31 | export default relativeUrls; 32 | -------------------------------------------------------------------------------- /plugins/search.ts: -------------------------------------------------------------------------------- 1 | import type Site from "../core/site.ts"; 2 | import type Searcher from "../core/searcher.ts"; 3 | 4 | /** 5 | * A plugin to add a search helper to the data 6 | * Installed by default 7 | * @see https://lume.land/plugins/search/ 8 | */ 9 | export function search() { 10 | return (site: Site) => { 11 | site.data("search", site.search); 12 | }; 13 | } 14 | 15 | export default search; 16 | 17 | /** Extends Data interface */ 18 | declare global { 19 | namespace Lume { 20 | export interface Data { 21 | /** 22 | * The searcher helper 23 | * @see https://lume.land/plugins/search/ 24 | */ 25 | search: Searcher; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /plugins/toml.ts: -------------------------------------------------------------------------------- 1 | import tomlLoader from "../core/loaders/toml.ts"; 2 | import { merge } from "../core/utils/object.ts"; 3 | 4 | import type Site from "../core/site.ts"; 5 | 6 | export interface Options { 7 | /** File extensions to load */ 8 | extensions?: string[]; 9 | 10 | /** Optional sub-extension for page files */ 11 | pageSubExtension?: string; 12 | } 13 | 14 | // Default options 15 | export const defaults: Options = { 16 | extensions: [".toml"], 17 | pageSubExtension: ".page", 18 | }; 19 | 20 | /** 21 | * A plugin to load TOML data files and pages 22 | * @see https://lume.land/plugins/toml/ 23 | */ 24 | export function toml(userOptions?: Options) { 25 | const options = merge(defaults, userOptions); 26 | 27 | return (site: Site) => { 28 | site.loadData(options.extensions, tomlLoader); 29 | site.loadPages(options.extensions, { 30 | loader: tomlLoader, 31 | pageSubExtension: options.pageSubExtension, 32 | }); 33 | }; 34 | } 35 | 36 | export default toml; 37 | -------------------------------------------------------------------------------- /plugins/url.ts: -------------------------------------------------------------------------------- 1 | import type Site from "../core/site.ts"; 2 | 3 | /** 4 | * A plugin to register the filters "url" and "htmlUrl" 5 | * for normalizing URLs in the templates 6 | * Installed by default 7 | * @see https://lume.land/plugins/url/ 8 | */ 9 | export function url() { 10 | return (site: Site) => { 11 | site.filter("url", url); 12 | site.filter("htmlUrl", htmlUrl); 13 | 14 | function url(path = "/", absolute = false): string { 15 | return typeof path === "string" ? site.url(path, absolute) : path; 16 | } 17 | 18 | function htmlUrl(html = "", absolute = false): string { 19 | return html.replaceAll( 20 | /\s(href|src)="([^"]+)"/g, 21 | (_match, attr, value) => ` ${attr}="${url(value, absolute)}"`, 22 | ); 23 | } 24 | }; 25 | } 26 | 27 | export default url; 28 | 29 | /** Extends Helpers interface */ 30 | declare global { 31 | namespace Lume { 32 | export interface Helpers { 33 | /** @see https://lume.land/plugins/url/#url-filter */ 34 | url: (path: string, absolute?: boolean) => string; 35 | 36 | /** @see https://lume.land/plugins/url/#htmlurl-filter */ 37 | htmlUrl: (html: string, absolute?: boolean) => string; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /plugins/yaml.ts: -------------------------------------------------------------------------------- 1 | import yamlLoader from "../core/loaders/yaml.ts"; 2 | import { merge } from "../core/utils/object.ts"; 3 | 4 | import type Site from "../core/site.ts"; 5 | 6 | export interface Options { 7 | /** File extensions to load */ 8 | extensions?: string[]; 9 | 10 | /** Optional sub-extension for page files */ 11 | pageSubExtension?: string; 12 | } 13 | 14 | // Default options 15 | export const defaults: Options = { 16 | extensions: [".yaml", ".yml"], 17 | }; 18 | 19 | /** 20 | * A plugin to load YAML data files and pages 21 | * Installed by default 22 | * @see https://lume.land/plugins/yaml/ 23 | */ 24 | export function yaml(userOptions?: Options) { 25 | const options = merge(defaults, userOptions); 26 | 27 | return (site: Site) => { 28 | site.loadData(options.extensions, yamlLoader); 29 | site.loadPages(options.extensions, { 30 | loader: yamlLoader, 31 | pageSubExtension: options.pageSubExtension, 32 | }); 33 | }; 34 | } 35 | 36 | export default yaml; 37 | -------------------------------------------------------------------------------- /tests/__snapshots__/cache_busting.test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`cache busting middleware 1`] = ` 4 | { 5 | body: "http://localhost/styles.css", 6 | headers: { 7 | "content-type": "text/plain;charset=UTF-8", 8 | }, 9 | method: "GET", 10 | request: "http://localhost/v1/styles.css", 11 | status: 200, 12 | statusText: "", 13 | } 14 | `; 15 | 16 | snapshot[`cache busting middleware 2`] = ` 17 | { 18 | body: "http://localhost/scripts.js", 19 | headers: { 20 | "content-type": "text/plain;charset=UTF-8", 21 | }, 22 | method: "GET", 23 | request: "http://localhost/v2873/scripts.js", 24 | status: 200, 25 | statusText: "", 26 | } 27 | `; 28 | -------------------------------------------------------------------------------- /tests/__snapshots__/check_urls.test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`check_urls plugin 1`] = ` 4 | [ 5 | { 6 | pages: [ 7 | "/", 8 | ], 9 | url: "/page1", 10 | }, 11 | { 12 | pages: [ 13 | "/", 14 | ], 15 | url: "/page1/", 16 | }, 17 | { 18 | pages: [ 19 | "/", 20 | ], 21 | url: "/page2", 22 | }, 23 | { 24 | pages: [ 25 | "/", 26 | ], 27 | url: "/page2/", 28 | }, 29 | ] 30 | `; 31 | 32 | snapshot[`check_urls plugin (strict mode) 1`] = ` 33 | [ 34 | { 35 | pages: [ 36 | "/", 37 | ], 38 | url: "/page1", 39 | }, 40 | { 41 | pages: [ 42 | "/", 43 | ], 44 | url: "/page1/", 45 | }, 46 | { 47 | pages: [ 48 | "/", 49 | ], 50 | url: "/page2", 51 | }, 52 | { 53 | pages: [ 54 | "/", 55 | ], 56 | url: "/page2/", 57 | }, 58 | { 59 | pages: [ 60 | "/", 61 | ], 62 | url: "/page3", 63 | }, 64 | { 65 | pages: [ 66 | "/", 67 | ], 68 | url: "/page3/", 69 | }, 70 | ] 71 | `; 72 | -------------------------------------------------------------------------------- /tests/__snapshots__/no_cache.test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`no cache middleware 1`] = ` 4 | { 5 | body: "Hello World", 6 | headers: { 7 | "cache-control": "no-cache no-store must-revalidate", 8 | "content-type": "text/plain;charset=UTF-8", 9 | }, 10 | method: "GET", 11 | request: "http://localhost/", 12 | status: 200, 13 | statusText: "", 14 | } 15 | `; 16 | -------------------------------------------------------------------------------- /tests/__snapshots__/no_cors.test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`no_cors middleware 1`] = ` 4 | { 5 | body: "OK", 6 | headers: { 7 | "access-control-allow-origin": "*", 8 | "content-type": "text/plain;charset=UTF-8", 9 | }, 10 | method: "GET", 11 | request: "http://localhost/", 12 | status: 200, 13 | statusText: "", 14 | } 15 | `; 16 | -------------------------------------------------------------------------------- /tests/assets/base_path/index.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Home 16 | Blog 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/assets/check_urls/index.md: -------------------------------------------------------------------------------- 1 | [Relative link to existing page](./page1/) 2 | [Link to existing page without trailin slash](/page1) 3 | 4 | [Link to non existing page](/page2/) 5 | [Relative link to non existing page without trailin slash](./page2) 6 | 7 | [Link to redirected page](/page3/) 8 | [Relative link to redirected page without trailin slash](./page3) -------------------------------------------------------------------------------- /tests/assets/check_urls/page1.md: -------------------------------------------------------------------------------- 1 | # Page 1 -------------------------------------------------------------------------------- /tests/assets/check_urls/page4.md: -------------------------------------------------------------------------------- 1 | --- 2 | oldUrl: /page3/ 3 | --- 4 | 5 | # Page 3 is now 4 -------------------------------------------------------------------------------- /tests/assets/code_highlight/index.md: -------------------------------------------------------------------------------- 1 | # Code highlight plugin testing 2 | 3 | ```html 4 |

This is a HTML code

5 | ``` 6 | 7 | ``` 8 | Not highlighted code 9 | ``` 10 | 11 | ```css 12 | p { 13 | color: red 14 | } 15 | ``` 16 | -------------------------------------------------------------------------------- /tests/assets/code_highlight/other.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Code inside a PRE
4 | Code inside a CODE 5 |
Not highlighted code
6 |
p { display: none }
7 | 8 | -------------------------------------------------------------------------------- /tests/assets/components/_components/eta_button.eta: -------------------------------------------------------------------------------- 1 | --- 2 | css: | 3 | .button_eta { 4 | color: white; 5 | background-color: blue; 6 | } 7 | --- 8 | 9 | -------------------------------------------------------------------------------- /tests/assets/components/_components/header/comp.vto: -------------------------------------------------------------------------------- 1 | --- 2 | title: "foo" 3 | --- 4 | 7 | -------------------------------------------------------------------------------- /tests/assets/components/_components/header/script.js: -------------------------------------------------------------------------------- 1 | document.getElementById('header')?.addEventListener('click', function() { 2 | alert('Header clicked'); 3 | }); -------------------------------------------------------------------------------- /tests/assets/components/_components/header/style.css: -------------------------------------------------------------------------------- 1 | .header { 2 | background-color: pink; 3 | } -------------------------------------------------------------------------------- /tests/assets/components/_components/inherit.njk: -------------------------------------------------------------------------------- 1 |

Inherit: {{ text }}

-------------------------------------------------------------------------------- /tests/assets/components/_components/jsx_button.jsx: -------------------------------------------------------------------------------- 1 | export const className = "button_jsx"; 2 | export const css = ` 3 | .button_jsx { 4 | color: white; 5 | background-color: blue; 6 | } 7 | `; 8 | 9 | export default function ({ text, className }) { 10 | return ; 11 | } 12 | -------------------------------------------------------------------------------- /tests/assets/components/_components/njk_button.njk: -------------------------------------------------------------------------------- 1 | --- 2 | className: button_njk 3 | css: | 4 | .button_njk { 5 | color: white; 6 | background-color: blue; 7 | } 8 | --- 9 | 10 | -------------------------------------------------------------------------------- /tests/assets/components/_components/not_inherit.njk: -------------------------------------------------------------------------------- 1 | --- 2 | inheritData: false 3 | --- 4 |

Not inherit: {{ text }}

5 | -------------------------------------------------------------------------------- /tests/assets/components/_components/vento_button.vto: -------------------------------------------------------------------------------- 1 | --- 2 | css: | 3 | .button_vto { 4 | color: white; 5 | background-color: blue; 6 | } 7 | --- 8 | -------------------------------------------------------------------------------- /tests/assets/components/_data.yml: -------------------------------------------------------------------------------- 1 | text: Hello from _data.yml -------------------------------------------------------------------------------- /tests/assets/components/index.vto: -------------------------------------------------------------------------------- 1 | index 2 | 3 | {{ comp header { title: "Hello world" } /}} 4 | {{ comp header /}} -------------------------------------------------------------------------------- /tests/assets/components/subfolder/_components/innerButton.js: -------------------------------------------------------------------------------- 1 | export default async function ({ comp, text }) { 2 | return `
${await comp.jsx_button({ text })}
`; 3 | } 4 | -------------------------------------------------------------------------------- /tests/assets/components/subfolder/_components/pug_button.pug: -------------------------------------------------------------------------------- 1 | --- 2 | css: | 3 | .button_pug { 4 | color: white; 5 | background-color: blue; 6 | } 7 | --- 8 | button(class="button_pug") #{text} 9 | -------------------------------------------------------------------------------- /tests/assets/components/subfolder/_components/ts_button.ts: -------------------------------------------------------------------------------- 1 | export const css = ` 2 | .button_ts { 3 | color: white; 4 | background-color: blue; 5 | } 6 | `; 7 | 8 | interface Props { 9 | text: string; 10 | } 11 | 12 | export default function ({ text }: Props) { 13 | return ``; 14 | } 15 | -------------------------------------------------------------------------------- /tests/assets/components/subfolder/index.njk: -------------------------------------------------------------------------------- 1 | index -------------------------------------------------------------------------------- /tests/assets/components_interop/_components/subtitle.vto: -------------------------------------------------------------------------------- 1 |

Children: {{ children ?? "empty" }}

2 |

Content: {{ content ?? "empty" }}

3 | -------------------------------------------------------------------------------- /tests/assets/components_interop/_components/title.tsx: -------------------------------------------------------------------------------- 1 | export default function ({ children, content }) { 2 | return <> 3 |

Children: {children ?? "empty"}

4 |

Content: {content ?? "empty"}

5 | 6 | } -------------------------------------------------------------------------------- /tests/assets/components_interop/index.vto: -------------------------------------------------------------------------------- 1 | {{ comp title }}Benvido!!{{ /comp }} 2 | {{ comp.title({ content: "Benvido2" })}} 3 | {{ comp.title({ children: "Benvido3" })}} 4 | 5 | {{ comp subtitle }}Benvido!!{{ /comp }} 6 | {{ comp.subtitle({ content: "Benvido2" })}} 7 | {{ comp.subtitle({ children: "Benvido3" })}} 8 | -------------------------------------------------------------------------------- /tests/assets/components_interop/index2.page.tsx: -------------------------------------------------------------------------------- 1 | export default function ({ comp }: Lume.Data) { 2 | return <> 3 | 4 |
5 | Click here 6 |
7 | Benvido}/> 8 |
9 |
10 | 11 |
12 | Click here 13 |
14 | 15 | 16 | } -------------------------------------------------------------------------------- /tests/assets/decap_cms/_data/decap_cms.yml: -------------------------------------------------------------------------------- 1 | backend: 2 | name: git-gateway 3 | branch: master 4 | 5 | media_folder: "img" 6 | public_folder: "/img" -------------------------------------------------------------------------------- /tests/assets/decap_cms/index.md: -------------------------------------------------------------------------------- 1 | # Hello world 2 | -------------------------------------------------------------------------------- /tests/assets/deno.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | // deno-fmt-ignore 3 | "tasks": { 4 | "foo": "echo bar", // A trailing comma and a comment for spicing tests up 5 | }, 6 | "importMap": "./import_map.json" 7 | } 8 | -------------------------------------------------------------------------------- /tests/assets/empty/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/empty/.gitkeep -------------------------------------------------------------------------------- /tests/assets/esbuild/_includes/layout.js: -------------------------------------------------------------------------------- 1 | export default function ({ content }) { 2 | return ` 3 | 4 | ${content} 5 | 6 | `; 7 | } 8 | -------------------------------------------------------------------------------- /tests/assets/esbuild/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar" 3 | } 4 | -------------------------------------------------------------------------------- /tests/assets/esbuild/main.d.ts: -------------------------------------------------------------------------------- 1 | export interface Foo { 2 | bar: string; 3 | } -------------------------------------------------------------------------------- /tests/assets/esbuild/main.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import toUppercase, { toLowercase } from "./modules/to_uppercase.ts"; 3 | import { Foo } from "./main.d.ts"; 4 | import data from "./data.json" with { type: "json" }; 5 | 6 | // https://github.com/lumeland/lume/issues/442 7 | import "https://esm.sh/v127/prop-types@15.8.1/denonext/prop-types.development.mjs"; 8 | 9 | document.querySelectorAll("h1")?.forEach((h1) => { 10 | h1.innerHTML = toUppercase(h1.innerHTML + data.foo); 11 | 12 | toLowercase(h1.innerHTML) 13 | .then(lower => { 14 | h1.innerHTML = lower; 15 | }); 16 | }); 17 | 18 | const foo: Foo = { bar: "baz" }; 19 | console.log(foo); 20 | -------------------------------------------------------------------------------- /tests/assets/esbuild/main.vto: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout.js 3 | --- 4 | Hello world -------------------------------------------------------------------------------- /tests/assets/esbuild/modules/to_uppercase.ts: -------------------------------------------------------------------------------- 1 | export default function toUppercase(text: string) { 2 | return text.toUpperCase(); 3 | } 4 | 5 | export async function toLowercase(text: string) { 6 | const { toLowercase } = await import("../other/to_lowercase.ts"); 7 | 8 | return toLowercase(text); 9 | } 10 | -------------------------------------------------------------------------------- /tests/assets/esbuild/other.page.ts: -------------------------------------------------------------------------------- 1 | export const url = "/other.ts"; 2 | 3 | export default function () { 4 | return ` 5 | /// 6 | import toUppercase from "./modules/to_uppercase.ts"; 7 | 8 | document.querySelectorAll(".other")?.forEach((el) => { 9 | el.innerHTML = toUppercase(el.innerHTML); 10 | }); 11 | `; 12 | } 13 | -------------------------------------------------------------------------------- /tests/assets/esbuild/other/_data.yml: -------------------------------------------------------------------------------- 1 | basename: "/foo/bar" -------------------------------------------------------------------------------- /tests/assets/esbuild/other/script.ts: -------------------------------------------------------------------------------- 1 | console.log("Hello from script.ts!"); 2 | -------------------------------------------------------------------------------- /tests/assets/esbuild/other/to_lowercase.ts: -------------------------------------------------------------------------------- 1 | export function toLowercase(text: string) { 2 | return text.toLowerCase(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/assets/esbuild_jsx/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "npm:react@18.2.0", 4 | "react-dom/client": "npm:react-dom@18.2.0/client" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/assets/esbuild_jsx/index.page.jsx: -------------------------------------------------------------------------------- 1 | /** @jsxImportSource npm:react@18.2.0 */ 2 | 3 | export default () => ( 4 | <> 5 |

6 |

test 123

7 | 8 | ); 9 | -------------------------------------------------------------------------------- /tests/assets/esbuild_jsx/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | 4 | ReactDOM.createRoot(document.getElementById("app")).render( 5 | 6 |
qqqqqqqqq
7 |
, 8 | ); 9 | -------------------------------------------------------------------------------- /tests/assets/eta/_includes/footer.eta: -------------------------------------------------------------------------------- 1 |
This is a footer of <%= name %>
-------------------------------------------------------------------------------- /tests/assets/eta/_includes/header.eta: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | -------------------------------------------------------------------------------- /tests/assets/eta/_includes/layout.eta: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%~ await includeAsync("/header", { title }) %> 4 | 5 | 8 | <%~ content %> 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/assets/eta/index.eta: -------------------------------------------------------------------------------- 1 | --- 2 | title: Eta example 3 | name: Timothy 4 | layout: layout.eta 5 | --- 6 |

<%= name %>'s Eta source code!

7 | 8 | <%~ await includeAsync("/footer", { name }) %> 9 | -------------------------------------------------------------------------------- /tests/assets/favicon/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/assets/favicon/index.vto: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hello world 3 | --- 4 | 5 | 6 | 7 | {{ title }} 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/assets/feed/404.md: -------------------------------------------------------------------------------- 1 | This page is exported to `/404.html`, not `/404/index.html` 2 | -------------------------------------------------------------------------------- /tests/assets/feed/page1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Page 1 3 | tags: page1 4 | draft: true 5 | --- 6 | 7 | Content of Page 1 8 | -------------------------------------------------------------------------------- /tests/assets/feed/page5.yaml: -------------------------------------------------------------------------------- 1 | title: Page **5** 2 | content: Content of Page 5 3 | date: 1979-06-21T23:45:00.000Z 4 | published: "1979-06-21T23:45:00.000Z" 5 | author: 6 | name: Óscar 7 | url: https://oscarotero.com -------------------------------------------------------------------------------- /tests/assets/feed/static.yml: -------------------------------------------------------------------------------- 1 | content: This yaml should be ignored because it's copied statically -------------------------------------------------------------------------------- /tests/assets/fff/date.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: FFF Date Test 3 | published: 2024-01-01 4 | --- 5 | 6 | The `published` should be used as `date`. 7 | -------------------------------------------------------------------------------- /tests/assets/fff/image.md: -------------------------------------------------------------------------------- 1 | --- 2 | alt: FFF Image Test 3 | images: 4 | - /my-image.png 5 | --- 6 | 7 | FFF Image Test 8 | -------------------------------------------------------------------------------- /tests/assets/fff/tags.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: 3 | - foo 4 | - bar 5 | categories: 6 | - baz 7 | - qux 8 | --- 9 | 10 | FFF Tags Test 11 | 12 | The `categories` should be merged into `tags`. 13 | -------------------------------------------------------------------------------- /tests/assets/frontmatter-only-comment.md: -------------------------------------------------------------------------------- 1 | --- 2 | # Nothing here. 3 | --- 4 | -------------------------------------------------------------------------------- /tests/assets/google-fonts/styles.css: -------------------------------------------------------------------------------- 1 | @layer { 2 | /* google-fonts */ 3 | } 4 | -------------------------------------------------------------------------------- /tests/assets/import_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "std/": "https://deno.land/std@0.121.0/" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/assets/inline/other favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/inline/other favicon.png -------------------------------------------------------------------------------- /tests/assets/inline/script.js: -------------------------------------------------------------------------------- 1 | console.log("Hello world"); 2 | const a = 1, _b = 1 < a, _c = a > 4; 3 | -------------------------------------------------------------------------------- /tests/assets/inline/styles.vto: -------------------------------------------------------------------------------- 1 | --- 2 | url: /styles.css 3 | --- 4 | 5 | body { 6 | background: white; 7 | color: #333; 8 | } -------------------------------------------------------------------------------- /tests/assets/json/_data.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "site": { 3 | "title": "Default title", // comment 4 | "description": "Default description" /* comment */ 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/assets/json/_includes/layout.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ title }} 4 | 5 | 6 |

{{ site.title }}

7 |

{{ site.description }}

8 |

{{ title }}

9 |

{{ content }}

10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/assets/json/index.page.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Title of the index", 3 | "content": "Content of the index", 4 | "date": "1979-06-21T23:45:00.000Z", 5 | "layout": "layout.vto" 6 | } 7 | -------------------------------------------------------------------------------- /tests/assets/json/page1.md: -------------------------------------------------------------------------------- 1 | { 2 | "title": "First page" 3 | } 4 | 5 | # Welcome 6 | -------------------------------------------------------------------------------- /tests/assets/json_ld/page-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | header: 3 | title: Title from page data 4 | cover: ./use-cover-as-meta-image.png 5 | jsonLd: 6 | "@type": "WebSite" 7 | url: "/" 8 | name: "=header.title" 9 | inLanguage: "gl" 10 | publisher: 11 | "@type": "Organization" 12 | name: "=header.title" 13 | logo: 14 | "@type": "ImageObject" 15 | url: "=cover" 16 | image: "/my-image.png" 17 | keywords: =keywords 18 | emptyThing: 19 | "@type": "EmptyThing" 20 | keywords: 21 | - "one" 22 | - "two" 23 | --- 24 | 25 | # Welcome to my website 26 | 27 | This is my first page using **Lume,** a static site generator for Deno. 28 | [test link](/test/) 29 | I hope you enjoy it. 30 | -------------------------------------------------------------------------------- /tests/assets/json_ld/page-2.vto: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | 5 | 6 | 7 | Pages without jsonld 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/assets/json_ld/page-3.page.ts: -------------------------------------------------------------------------------- 1 | import type { JsonldData } from "../../../plugins/json_ld.ts"; 2 | 3 | export const jsonLd: JsonldData = { 4 | "@type": "WebSite", 5 | url: "/", 6 | headline: "Óscar Otero - Web designer and developer", 7 | description: "I’m just a designer and web developer", 8 | name: "Óscar Otero", 9 | author: { 10 | "@type": "Person", 11 | name: "Óscar Otero", 12 | }, 13 | } 14 | 15 | export default function () { 16 | return "Page content"; 17 | } -------------------------------------------------------------------------------- /tests/assets/jsx/_components/button.jsx: -------------------------------------------------------------------------------- 1 | export default function ({ type }) { 2 | return ; 3 | } 4 | -------------------------------------------------------------------------------- /tests/assets/jsx/_includes/hello.jsx: -------------------------------------------------------------------------------- 1 | export default function ({ children }) { 2 | return
{children}
; 3 | } 4 | -------------------------------------------------------------------------------- /tests/assets/jsx/_includes/layout.jsx: -------------------------------------------------------------------------------- 1 | export default ({ children, title }) => ( 2 | 3 | 4 | {title} 5 | 6 | 7 |
8 | {children} 9 |
10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /tests/assets/jsx/index.page.jsx: -------------------------------------------------------------------------------- 1 | export const layout = "layout.jsx"; 2 | export const title = "This is the title"; 3 | 4 | // Export a React component 5 | export default ( 6 | <> 7 |

Hello world

8 |

This is a JSX page

9 | 10 | ); 11 | -------------------------------------------------------------------------------- /tests/assets/jsx/multiple.page.jsx: -------------------------------------------------------------------------------- 1 | import { assert } from "../../../deps/assert.ts"; 2 | export const layout = "layout.jsx"; 3 | 4 | // Export a function 5 | export default function* ({ url }) { 6 | const pages = [1, 2]; 7 | 8 | assert(url === "/multiple/"); 9 | 10 | for (const page of pages) { 11 | yield { 12 | url: `/page/${page}/`, 13 | content:
{page}
, 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/assets/jsx/with-function.page.jsx: -------------------------------------------------------------------------------- 1 | export const layout = "layout.jsx"; 2 | export const title = "This is the title"; 3 | 4 | // Export a function 5 | export default ({ title }, { url }) => ( 6 | <> 7 |

{title}

8 |

9 | This is a JSX page Go to home 10 |

11 | 12 | ); 13 | -------------------------------------------------------------------------------- /tests/assets/jsx/with-markdown.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout.jsx 3 | title: Markdown combined with JSX 4 | --- 5 | 6 | # Hello world{.foo} 7 | 8 | This is a **markdown** text. [Go home](/) 9 | -------------------------------------------------------------------------------- /tests/assets/jsx_preact/_components/button.jsx: -------------------------------------------------------------------------------- 1 | export default function ({ type }) { 2 | return ; 3 | } 4 | -------------------------------------------------------------------------------- /tests/assets/jsx_preact/_includes/hello.jsx: -------------------------------------------------------------------------------- 1 | export default function ({ children }) { 2 | return
{children}
; 3 | } 4 | -------------------------------------------------------------------------------- /tests/assets/jsx_preact/_includes/layout.jsx: -------------------------------------------------------------------------------- 1 | export default ({ children, title }) => ( 2 | 3 | 4 | {title} 5 | 6 | 7 |
8 | {children} 9 |
10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /tests/assets/jsx_preact/index.page.jsx: -------------------------------------------------------------------------------- 1 | export const layout = "layout.jsx"; 2 | export const title = "This is the title"; 3 | 4 | // Export a component 5 | export default ( 6 | <> 7 |

Hello world

8 |

This is a JSX page

9 | 10 | ); 11 | -------------------------------------------------------------------------------- /tests/assets/jsx_preact/multiple.page.jsx: -------------------------------------------------------------------------------- 1 | import { assert } from "../../../deps/assert.ts"; 2 | export const layout = "layout.jsx"; 3 | 4 | // Export a function 5 | export default function* ({ url }) { 6 | const pages = [1, 2]; 7 | 8 | assert(url === "/multiple/"); 9 | 10 | for (const page of pages) { 11 | yield { 12 | url: `/page/${page}/`, 13 | content:
{page}
, 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/assets/jsx_preact/with-comp.page.njk: -------------------------------------------------------------------------------- 1 | {{ comp.button('primary') | safe }} 2 | -------------------------------------------------------------------------------- /tests/assets/jsx_preact/with-function.page.jsx: -------------------------------------------------------------------------------- 1 | export const layout = "layout.jsx"; 2 | export const title = "This is the title"; 3 | 4 | // Export a function 5 | export default ({ title }, { url }) => ( 6 | <> 7 |

{title}

8 |

9 | This is a JSX page Go to home 10 |

11 | 12 | ); 13 | -------------------------------------------------------------------------------- /tests/assets/jsx_preact/with-markdown.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout.jsx 3 | title: Markdown combined with JSX 4 | --- 5 | 6 | # Hello world{.foo} 7 | 8 | This is a **markdown** text. [Go home](/) 9 | 10 | ```css 11 | body { 12 | color: blue; 13 | } 14 | ``` 15 | -------------------------------------------------------------------------------- /tests/assets/katex/index.md: -------------------------------------------------------------------------------- 1 | # Page title 2 | 3 | ```math 4 | c = \pm\sqrt{a^2 + b^2} 5 | ``` 6 | 7 |
8 | % \f is defined as #1f(#2) using the macro 9 | \f\relax{x} = \int_{-\infty}^\infty 10 | \f\hat\xi\,e^{2 \pi i \xi x} 11 | \,d\xi 12 |
13 | 14 |
15 | This is some text $math \frac12$ other text $\unsupported$ 16 | 17 | Other node \[ \text{displaymath} \frac{1}{2} \] blah $$ \int_2^3 $$ 18 | 19 | and some more text \(and math\) blah. And $math with a 20 | \$ sign$. 21 |
22 |   Stuff in a $pre tag$
23 |   
24 |

An AMS environment without $$…$$ delimiters.

25 |

\begin{equation} \begin{split} a &=b+c\\ &=e+f \end{split} \end{equation}

26 |
27 | -------------------------------------------------------------------------------- /tests/assets/layouts/_includes/layout-2.vto: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout-3.vto 3 | css_file: 4 | - /file-1.css 5 | - /file-2.css 6 | --- 7 | 8 |

From Layout 2

9 | {{ css_file.join(",") }} 10 | 11 | {{ content }} 12 | -------------------------------------------------------------------------------- /tests/assets/layouts/_includes/layout-3.vto: -------------------------------------------------------------------------------- 1 | --- 2 | css_file: 3 | - /file-1.css 4 | - /file-2.css 5 | - /file-3.css 6 | --- 7 | 8 |

From Layout 3

9 | {{ css_file.join(",") }} 10 | 11 | {{ content }} 12 | -------------------------------------------------------------------------------- /tests/assets/layouts/_includes/page.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ content }} 4 | 5 | -------------------------------------------------------------------------------- /tests/assets/layouts/_includes/style.vto: -------------------------------------------------------------------------------- 1 | body.style { 2 | {{ content }} 3 | } -------------------------------------------------------------------------------- /tests/assets/layouts/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page.vto 3 | --- 4 | 5 | # Hello world 6 | -------------------------------------------------------------------------------- /tests/assets/layouts/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout-2.vto 3 | css_file: /file-1.css 4 | mergedKeys: 5 | css_file: stringArray 6 | --- 7 | 8 | Content 9 | -------------------------------------------------------------------------------- /tests/assets/layouts/styles.2.css: -------------------------------------------------------------------------------- 1 | --- 2 | layout: style.vto 3 | --- 4 | p { 5 | color: red; 6 | } -------------------------------------------------------------------------------- /tests/assets/layouts/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: blue; 3 | } -------------------------------------------------------------------------------- /tests/assets/lightningcss/_includes/variables.css: -------------------------------------------------------------------------------- 1 | ::root { 2 | --color: #333; 3 | --background: #fff; 4 | --font-family: sans-serif; 5 | } -------------------------------------------------------------------------------- /tests/assets/lightningcss/index.css: -------------------------------------------------------------------------------- 1 | @import "variables.css"; 2 | @import "./text.css"; 3 | -------------------------------------------------------------------------------- /tests/assets/lightningcss/text.css: -------------------------------------------------------------------------------- 1 | @import "npm:modern-normalize@2.0.0"; 2 | 3 | .text { 4 | font-family: var(--font-family); 5 | 6 | & p { 7 | color: var(--color); 8 | box-shadow: 0 0 0.5em var(--background); 9 | backface-visibility: hidden; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/assets/markdown/basic.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Basic page 3 | --- 4 | 5 | # Normal page 6 | 7 | - List 8 | - Of 9 | - Elements 10 | -------------------------------------------------------------------------------- /tests/assets/markdown/empty.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | --- 4 | -------------------------------------------------------------------------------- /tests/assets/markdown/footnote.md: -------------------------------------------------------------------------------- 1 | Here is a footnote reference,[^1] and another.[^longnote] 2 | 3 | [^1]: Here is the footnote. 4 | 5 | [^longnote]: Here's one with multiple blocks. 6 | 7 | 8 | Subsequent paragraphs are indented to show that they 9 | belong to the previous footnote. 10 | -------------------------------------------------------------------------------- /tests/assets/markdown/with-attributes.md: -------------------------------------------------------------------------------- 1 | [link](#foo){target=_blank} 2 | -------------------------------------------------------------------------------- /tests/assets/markdown/with-code.md: -------------------------------------------------------------------------------- 1 | ```html 2 |

Example

3 | ``` 4 | 5 | ``` 6 |

Example without color highlight

7 | ``` 8 | 9 | Not indented code 10 | -------------------------------------------------------------------------------- /tests/assets/markdown/with-deflist.md: -------------------------------------------------------------------------------- 1 | 2 | This is a definition title 3 | : And this the description 4 | : Other description 5 | 6 | Other title 7 | : And other description 8 | -------------------------------------------------------------------------------- /tests/assets/markdown/with-filter.vto: -------------------------------------------------------------------------------- 1 | --- 2 | title: Module **example** 3 | description: | 4 | Welcome to this [page](/) 5 | --- 6 | 7 |

{{ title |> md(true) }}

8 | 9 |
{{ description |> md }}
10 | -------------------------------------------------------------------------------- /tests/assets/markdown/with-module.page.js: -------------------------------------------------------------------------------- 1 | export const title = "Module example"; 2 | export const templateEngine = "js,md"; 3 | 4 | export default function ({ title }) { 5 | return ` 6 | # ${title} 7 | 8 | [Back to home](/) 9 | `; 10 | } 11 | -------------------------------------------------------------------------------- /tests/assets/markdown/with-vento.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Module **example** 3 | templateEngine: vto,md 4 | myData: 5 | one: un 6 | two: dous 7 | three: tres 8 | --- 9 | 10 | # {{ title }} 11 | 12 | Foo 13 | 14 | {{ for title, no of myData }} 15 | - {{ title }}: [{{ no }}](/items/{{ no }}.html) 16 | {{ /for }} 17 | -------------------------------------------------------------------------------- /tests/assets/mdx/_components/Header.jsx: -------------------------------------------------------------------------------- 1 | export default function Header({ title, description }) { 2 | return ( 3 |
4 |

{title}

5 |

{description}

6 |
7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /tests/assets/mdx/_includes/Image.tsx: -------------------------------------------------------------------------------- 1 | interface Options { 2 | src: string; 3 | alt: string; 4 | } 5 | 6 | export default function Image({ src, alt }: Options) { 7 | return {alt}; 8 | } 9 | -------------------------------------------------------------------------------- /tests/assets/mdx/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hello world 3 | description: This is a description 4 | --- 5 | import Image from "./_includes/Image.tsx"; 6 | 7 | 8 | 9 | ## Hello world 10 | 11 | This is a markdown file with the title **{ title }**. 12 | 13 | foo 14 | -------------------------------------------------------------------------------- /tests/assets/mdx/mdx-filter.page.ts: -------------------------------------------------------------------------------- 1 | import "../../../types.ts"; 2 | 3 | export const title = "mdx filter example"; 4 | export default async (data: Lume.Data, { mdx }: Lume.Helpers) => 5 | await mdx( 6 | ` 7 | --- 8 | title: Hello world 9 | description: This is a description 10 | --- 11 | import Image from "./_includes/Image.tsx"; 12 | 13 | 14 | 15 | ## Hello world 16 | 17 | This is a markdown file with the title **{ title }**. 18 | 19 | foo 20 | `, 21 | { 22 | ...data, 23 | title: "Hello world", 24 | description: "This is a description", 25 | }, 26 | ); 27 | -------------------------------------------------------------------------------- /tests/assets/metas/_data.js: -------------------------------------------------------------------------------- 1 | export const metas = { 2 | site: "My site", 3 | title: "My title", 4 | description: (data) => data.excerpt?.toUpperCase(), 5 | image: "/my-image.png", 6 | icon: "/my-icon.png", 7 | robots: false, 8 | keywords: [ 9 | "one", 10 | "two", 11 | ], 12 | twitter: "@myUser", 13 | lang: "gl", 14 | color: "black", 15 | }; 16 | -------------------------------------------------------------------------------- /tests/assets/metas/page-1.vto: -------------------------------------------------------------------------------- 1 | --- 2 | metas: 3 | title: Custom title 4 | description: | 5 | A very long custom description with a lot of text 6 | that will be truncated to a maximum of 155 characters and then append "…" to the end. 7 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. 8 | robots: true 9 | color: ["#ccc", "#333"] 10 | "twitter:label1": Reading time 11 | "twitter:data1": 1 minute 12 | generator: "Lume testing" 13 | --- 14 | 15 | 16 | 17 | 18 | Hello world 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/assets/metas/page-2.vto: -------------------------------------------------------------------------------- 1 | --- 2 | tags: ["one", "two", "three"] 3 | date: 2025-06-21 23:45:00 4 | metas: 5 | title: Relative **paths** 6 | description: Tests the use of relative path (to page.data.url) when filling out the og:image or og:icon URL 7 | image: ./my-image.png 8 | icon: ./my-icon.png 9 | "twitter:label1": Reading time 10 | "twitter:data1": 1 minute 11 | "article:published_time": "=date" 12 | "article:modified_time": "=date" 13 | "article:author": "=author" 14 | "article:section": "=article-type" 15 | "article:tag": "=tags" 16 | --- 17 | 18 | 19 | 20 | 21 | Hello world 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/assets/metas/page-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | header: 3 | title: Title from page data 4 | cover: ./use-cover-as-meta-image.png 5 | robots: this robots will be overrided 6 | metas: 7 | title: "=header.title" 8 | robots: "=robots" 9 | image: "=cover" 10 | "twitter:label1": Reading time 11 | "twitter:data1": 1 minute 12 | fediverse: "@lume@fosstodon.org" 13 | --- 14 | 15 | This is page excerpt will be used as meta description. 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/assets/minify_html/index.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 22 | 23 | 24 |

Hello World

25 | 31 | 32 | -------------------------------------------------------------------------------- /tests/assets/module/_includes/layout.js: -------------------------------------------------------------------------------- 1 | export const title = "Default title"; 2 | 3 | export default function ({ content, title }) { 4 | return ` 5 | 6 | 7 | 8 | 9 | ${title} 10 | 11 | 12 |
${content}
13 | 14 | 15 | `; 16 | } 17 | -------------------------------------------------------------------------------- /tests/assets/module/multiple-async.page.js: -------------------------------------------------------------------------------- 1 | export const tags = "autogenerated"; 2 | export const layout = "layout.js"; 3 | 4 | export default async function* () { 5 | const pages = [1, 2]; 6 | 7 | for (const num of pages) { 8 | yield new Promise((res) => 9 | setTimeout(() => 10 | res({ 11 | title: `Multiple page ${num}`, 12 | url: (page) => `/async/${page.data.num}/`, 13 | content: `Async Page ${num}`, 14 | num, 15 | }), 100) 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/assets/module/multiple.page.js: -------------------------------------------------------------------------------- 1 | export const tags = ["multiple"]; 2 | export const layout = "layout.js"; 3 | 4 | export default function* () { 5 | const pages = [1, 2, 3]; 6 | 7 | for (const page of pages) { 8 | const content = { 9 | url: `/multiple/${page}.html`, 10 | content: `Content page ${page}`, 11 | }; 12 | 13 | // The first page has a custom title 14 | if (page === 1) { 15 | content.title = "Page 1"; 16 | } 17 | 18 | yield content; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/assets/module/simple.page.js: -------------------------------------------------------------------------------- 1 | export const title = "Module example"; 2 | export const layout = "layout.js"; 3 | export const url = "/simple-page-new-permalink.html"; 4 | 5 | const content = ` 6 |

This is a simple page.

7 | Go to home page 8 | Go to simple page 9 | `; 10 | 11 | export default function ({ title }, { url, htmlUrl }) { 12 | return ` 13 |

${title}

14 | 17 |
18 | ${htmlUrl(content)} 19 |
20 | `; 21 | } 22 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "unmatchedLangUrl": "en" 3 | } 4 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/_includes/layout.vto: -------------------------------------------------------------------------------- 1 | 9 | {{ for item of results }} 10 |
  • {{ item.title }}
  • 11 | {{ /for }} 12 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | lang: [en, gl] 3 | unmatchedLangUrl: /lang-selector 4 | title: Hello world 5 | gl: 6 | title: Ola mundo 7 | content: Ola mundo 8 | --- 9 | 10 | Hello world 11 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/lang-selector.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Language Selector Page 3 | unmatchedLangUrl: null 4 | --- 5 | 6 | Select your languages... 7 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/other.vto: -------------------------------------------------------------------------------- 1 | --- 2 | lang: [en, fr] 3 | title: Other 4 | metas: 5 | subtitle: English subtitle 6 | description: Common description 7 | en: 8 | url: /other-page-english/ 9 | fr: 10 | title: Autre 11 | metas: 12 | subtitle: Sous-titre français 13 | url: /other-page-french.html 14 | --- 15 |

    {{ metas.subtitle }}

    16 |

    {{ metas.description }}

    17 |

    Other page Link to index in Galego

    18 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/pages/page1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: page-it 3 | lang: it 4 | id: page1 5 | url: /pagina-1/ 6 | --- 7 | 8 | Pagina 1 9 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/pages/page1_en.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: page-en 3 | lang: en 4 | id: page1 5 | url: /page-one/ 6 | --- 7 | 8 | Page 1 9 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/pages/page1_gl.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: page-gl 3 | lang: gl 4 | id: page1 5 | url: /paxina-un/ 6 | --- 7 | 8 | Páxina 1 9 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/pagination.page.js: -------------------------------------------------------------------------------- 1 | export const layout = "layout.vto"; 2 | 3 | export default function* ({ search, paginate }) { 4 | for (const p of paginate(search.pages("lang=gl", "url=asc"))) { 5 | yield { 6 | ...p, 7 | lang: "gl", 8 | id: `page-${p.pagination.page}`, 9 | }; 10 | } 11 | for (const p of paginate(search.pages("lang=en", "url=asc"))) { 12 | yield { 13 | ...p, 14 | lang: "en", 15 | id: `page-${p.pagination.page}`, 16 | }; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/types/article.md: -------------------------------------------------------------------------------- 1 | --- 2 | lang: it 3 | type: article 4 | id: 1 5 | title: article 1 6 | --- 7 | 8 | Questo è l'articolo 1 9 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/types/article_gl.md: -------------------------------------------------------------------------------- 1 | --- 2 | lang: gl 3 | type: article 4 | id: 1 5 | title: article 1 6 | --- 7 | 8 | Este é o artigo 1 9 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/types/post.md: -------------------------------------------------------------------------------- 1 | --- 2 | lang: en 3 | type: post 4 | id: 1 5 | title: post 1 6 | --- 7 | 8 | This is the post 1 9 | -------------------------------------------------------------------------------- /tests/assets/multilanguage/types/post_gl.md: -------------------------------------------------------------------------------- 1 | --- 2 | lang: gl 3 | type: post 4 | id: 1 5 | title: post 1 6 | --- 7 | 8 | Este é o post 1 9 | -------------------------------------------------------------------------------- /tests/assets/nav/_data.yml: -------------------------------------------------------------------------------- 1 | layout: main.vto -------------------------------------------------------------------------------- /tests/assets/nav/_includes/main.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ title }} 4 | 5 | 6 | 7 | {{ set previous = nav.previousPage(url) }} 8 | {{ if previous }} 9 | Previous {{ previous.basename }} 10 | {{ /if }} 11 | {{ set next = nav.nextPage(url) }} 12 | {{ if next }} 13 | Next {{ next.basename }} 14 | {{ /if }} 15 |
    16 | 23 | 24 |

    {{ basename }}

    25 | 26 | 35 | 36 | -------------------------------------------------------------------------------- /tests/assets/nav/_includes/step.vto: -------------------------------------------------------------------------------- 1 | {{ if item.data.url }} 2 | 3 | {{ item.data.basename }} 4 | {{ if item.data.url === url }} 5 | (current) 6 | {{ /if }} 7 | 8 | {{ else }} 9 | {{ item.data.basename }} 10 | {{ /if }} 11 | 12 | {{ if item.children }} 13 |
      14 | {{ for child of item.children }} 15 |
    • 16 | {{ include "./step.vto" { item: child } }} 17 |
    • 18 | {{ /for }} 19 |
    20 | {{ /if }} 21 | -------------------------------------------------------------------------------- /tests/assets/nav/docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /tests/assets/nav/docs/0-pages/1-first.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /tests/assets/nav/docs/0-pages/2-second.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /tests/assets/nav/docs/0-pages/3-pages.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /tests/assets/nav/docs/0-pages/3-pages/1.first.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /tests/assets/nav/docs/0-pages/3-pages/2.second.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /tests/assets/nav/docs/0-pages/4-pages/1-pages/1-first.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /tests/assets/nav/docs/0-pages/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /tests/assets/nav/docs/1-about-docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | -------------------------------------------------------------------------------- /tests/assets/nav/index.vto: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /tests/assets/normal/404.md: -------------------------------------------------------------------------------- 1 | This page is exported to `/404.html`, not `/404/index.html` 2 | -------------------------------------------------------------------------------- /tests/assets/normal/_data.yml: -------------------------------------------------------------------------------- 1 | site: Default site name 2 | 3 | mergedKeys: 4 | tags: stringArray 5 | metas: object 6 | imagick: array 7 | 8 | metas: 9 | title: Default title 10 | description: Default description 11 | 12 | imagick: 13 | resize: [400, 300] 14 | suffix: -small -------------------------------------------------------------------------------- /tests/assets/normal/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/normal/favicon.png -------------------------------------------------------------------------------- /tests/assets/normal/images/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/normal/images/avatar.jpg -------------------------------------------------------------------------------- /tests/assets/normal/page5.yaml: -------------------------------------------------------------------------------- 1 | title: Page 5 2 | content: Content of Page 5 3 | date: 1979-06-21T23:45:00.000Z -------------------------------------------------------------------------------- /tests/assets/normal/pages/2020-06-21_page2.page.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Page 2", 3 | "url": "/overrided-page2/", 4 | "content": "Content of Page 2", 5 | "metas": { 6 | "image": "/images/avatar.jpg" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/assets/normal/pages/2021-01-02-18-32_page4.page.ts: -------------------------------------------------------------------------------- 1 | import { Data } from "../../../../core/file.ts"; 2 | 3 | export const title = "Page 4"; 4 | export const site = "Overrided site name"; 5 | 6 | interface PageData extends Data { 7 | title: string; 8 | site: string; 9 | } 10 | 11 | export default function ({ title, site, page }: PageData) { 12 | return Promise.resolve( 13 | `Content of ${title} in ${site}, from the file ${ 14 | page.src.path + page.src.ext 15 | }`, 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /tests/assets/normal/pages/_data.yml: -------------------------------------------------------------------------------- 1 | site: Folder overrided site name 2 | tags: sub-pages -------------------------------------------------------------------------------- /tests/assets/normal/pages/_data/colors.yml: -------------------------------------------------------------------------------- 1 | - "#ff0000" 2 | - "#0000ff" 3 | - "#00ff00" -------------------------------------------------------------------------------- /tests/assets/normal/pages/_data/documents.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | title: "Document 1", 4 | }, 5 | { 6 | title: "Document 2", 7 | }, 8 | { 9 | title: "Document 3", 10 | }, 11 | ]; 12 | -------------------------------------------------------------------------------- /tests/assets/normal/pages/_data/drinks.js: -------------------------------------------------------------------------------- 1 | export const alcoholic = [ 2 | "gin-tonic", 3 | "gin-soda", 4 | ]; 5 | export const others = [ 6 | "coffee", 7 | "tea", 8 | ]; 9 | -------------------------------------------------------------------------------- /tests/assets/normal/pages/_data/names.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Oscar", 3 | "Laura" 4 | ] 5 | -------------------------------------------------------------------------------- /tests/assets/normal/pages/ghost/2021-12-29-page6.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Page 6 3 | tags: page6 4 | date: 2022-01-01 5 | --- 6 | 7 | Content of Page 6 8 | -------------------------------------------------------------------------------- /tests/assets/normal/pages/ghost/_data.yml: -------------------------------------------------------------------------------- 1 | basename: "" -------------------------------------------------------------------------------- /tests/assets/normal/pages/page1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Page 1 3 | tags: page1 4 | draft: true 5 | 6 | metas: 7 | title: Custom title 8 | 9 | imagick: 10 | resize: [900, 900] 11 | suffix: -big 12 | --- 13 | 14 | Content of Page 1 15 | -------------------------------------------------------------------------------- /tests/assets/normal/pages/page3.page.js: -------------------------------------------------------------------------------- 1 | export const title = "Page 3"; 2 | 3 | export const date = new Date(2020, 0, 1); 4 | 5 | export function url() { 6 | return "/page_" + (1 + 2) + "/"; 7 | } 8 | 9 | export default `Content of ${title}`; 10 | -------------------------------------------------------------------------------- /tests/assets/normal/pages/subpage/_data.yml: -------------------------------------------------------------------------------- 1 | tags: sub-sub-pages 2 | basename: new-name -------------------------------------------------------------------------------- /tests/assets/normal/pages/subpage/page7.page.js: -------------------------------------------------------------------------------- 1 | export const tags = "page7"; 2 | export const date = "2022-01-02"; 3 | export const content = "Content of Page 7"; 4 | -------------------------------------------------------------------------------- /tests/assets/normal/static.yml: -------------------------------------------------------------------------------- 1 | content: This is a page -------------------------------------------------------------------------------- /tests/assets/normal/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | color: black; 4 | } 5 | -------------------------------------------------------------------------------- /tests/assets/nunjucks/_components/Button.ts: -------------------------------------------------------------------------------- 1 | interface Props { 2 | content: string; 3 | className?: string; 4 | } 5 | 6 | export default function ({ content, className }: Props) { 7 | return ``; 8 | } 9 | 10 | export const css = ` 11 | button { 12 | background-color: blue; 13 | } 14 | `; 15 | 16 | export const js = ` 17 | document.querySelectorAll("button").forEach(button => { 18 | button.addEventListener("click", () => { 19 | alert("Hello world!"); 20 | }); 21 | }); 22 | `; 23 | -------------------------------------------------------------------------------- /tests/assets/nunjucks/_components/icon/User.njk: -------------------------------------------------------------------------------- 1 | --- 2 | css: | 3 | icon { 4 | width: 48px; 5 | height: 48px; 6 | } 7 | js: | 8 | console.log("Hello world, from the icon/User component"); 9 | --- 10 | 11 | {{ content }} 12 | 13 | -------------------------------------------------------------------------------- /tests/assets/nunjucks/_data.yml: -------------------------------------------------------------------------------- 1 | layout: basic.njk 2 | -------------------------------------------------------------------------------- /tests/assets/nunjucks/_includes/basic.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ title }} 7 | 8 | 9 |
    {{ content | safe }}
    10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/assets/nunjucks/_includes/partial.njk: -------------------------------------------------------------------------------- 1 |
      2 |
    • This is a partial
    • 3 |
    • {{ "async helper in a partial" | returnAsync }}
    • 4 |
    -------------------------------------------------------------------------------- /tests/assets/nunjucks/components.njk: -------------------------------------------------------------------------------- 1 | {% comp "Button" %} 2 | button without arguments 3 | {% endcomp %} 4 | 5 | {{ comp.Button({ content: "button content" }) | await | safe }} 6 | {{ comp.icon.User({ content: "this is John", name:"John" }) | await | safe }} 7 | 8 | {% comp "Button", className="my-button" %} 9 | button content 10 | 11 | {% comp "icon.user", name="John" %} 12 | this is John 13 | {% endcomp %} 14 | {% endcomp %} 15 | -------------------------------------------------------------------------------- /tests/assets/nunjucks/data.njk: -------------------------------------------------------------------------------- 1 | --- 2 | url: /data.json 3 | layout: null 4 | colors: 5 | - red 6 | - green 7 | - blue 8 | --- 9 | 10 | {{ colors | dump | safe }} 11 | -------------------------------------------------------------------------------- /tests/assets/nunjucks/empty.njk: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | --- -------------------------------------------------------------------------------- /tests/assets/nunjucks/index.njk: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hello World 3 | layout: ./_includes/basic.njk 4 | --- 5 | 6 |

    {{ title }}

    7 | -------------------------------------------------------------------------------- /tests/assets/nunjucks/njk-filter.page.js: -------------------------------------------------------------------------------- 1 | export const title = "njk filter example"; 2 | 3 | export default function (data, { njk }) { 4 | const content = `

    {{ title | upper }}

    `; 5 | return njk(content, data); 6 | } 7 | -------------------------------------------------------------------------------- /tests/assets/nunjucks/with-helpers.njk: -------------------------------------------------------------------------------- 1 | --- 2 | title: The title 3 | --- 4 |

    {% upperCase title %}

    5 |

    {% upperCase "The title" %}

    6 |

    {% upperCaseAsync title %}

    7 |

    {% upperCaseAsync "The title" %}

    8 |

    {% upperCaseBody %}The title{% endupperCaseBody %}

    9 |

    {% upperCaseBody %}{{ title }}{% endupperCaseBody %}

    10 |

    {% upperCaseBodyAsync %}The title{% endupperCaseBodyAsync %}

    11 |

    {% upperCaseBodyAsync %}{{ title }}{% endupperCaseBodyAsync %}

    12 |
    {{ "hello" | returnAsync }}
    13 | 14 | {% include "partial.njk" %} 15 | {% include "./_includes/partial.njk" %} 16 | 17 | {{ "title" | fromPage }} 18 | {{ "title" | fromPageAsync }} 19 |

    {% fromPageTagAsync %}title{% endfromPageTagAsync %}

    -------------------------------------------------------------------------------- /tests/assets/og_images/_data.yml: -------------------------------------------------------------------------------- 1 | openGraphLayout: og_template.jsx 2 | -------------------------------------------------------------------------------- /tests/assets/og_images/_includes/og_template.jsx: -------------------------------------------------------------------------------- 1 | /** @jsxImportSource npm:react@18.2.0 */ 2 | 3 | export default function (data) { 4 | return ( 5 |
    16 |
    24 |

    25 | {data.title} 26 |

    27 |
    28 | {data.description} 29 |
    30 |
    31 |
    32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /tests/assets/og_images/page1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Page 1 3 | description: Description of page 1 4 | --- 5 | 6 | This is the page 1 7 | -------------------------------------------------------------------------------- /tests/assets/pagefind/_includes/main.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ title }} 8 | 9 | 10 | 11 | {{ content }} 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/assets/pagefind/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: First page 3 | layout: main.vto 4 | --- 5 | 6 | # Hello world 7 | 8 | This is the first page 9 | -------------------------------------------------------------------------------- /tests/assets/pagefind/page2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Second page 3 | layout: main.vto 4 | --- 5 | 6 | # Other page 7 | 8 | This is the second page 9 | -------------------------------------------------------------------------------- /tests/assets/picture/index.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Demo 5 | 6 | 7 | 8 | 9 | 10 |
    11 | 12 |
    13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
    27 | 28 |
    29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/assets/picture/kevin schmid unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/picture/kevin schmid unsplash.jpg -------------------------------------------------------------------------------- /tests/assets/plaintext/index.vto: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Hello **world**" 3 | description: | 4 | This is a description
    5 | in multiple lines 6 | 7 | - With 8 | - list 9 | - elements 10 | - [and links](#url) 11 | --- 12 | 13 | 14 | 15 | 16 | 17 | 18 | {{ title |> plaintext }} 19 | 20 | 21 | 22 |

    {{ title |> md(true) }}

    23 | 24 | {{ description |> md }} 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/assets/postcss/_includes/components/foo.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /tests/assets/postcss/_includes/variables.css: -------------------------------------------------------------------------------- 1 | @import "./components/foo.css"; 2 | 3 | ::root { 4 | --color: #333; 5 | --background: #fff; 6 | --font-family: sans-serif; 7 | } -------------------------------------------------------------------------------- /tests/assets/postcss/index.min.css: -------------------------------------------------------------------------------- 1 | @import "variables.css"; 2 | @import "./text.css"; 3 | -------------------------------------------------------------------------------- /tests/assets/postcss/text.css: -------------------------------------------------------------------------------- 1 | @import "npm:modern-normalize@2.0.0"; 2 | 3 | .text { 4 | font-family: var(--font-family); 5 | 6 | & p { 7 | color: var(--color); 8 | box-shadow: 0 0 0.5em var(--background); 9 | backdrop-filter: blur(2px); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/assets/prism/index.md: -------------------------------------------------------------------------------- 1 | # Hello world 2 | 3 | ```css 4 | body { 5 | background: red; 6 | } 7 | ``` 8 | 9 | ```less 10 | @width: 10px; 11 | @height: @width + 10px; 12 | 13 | #header { 14 | width: @width; 15 | height: @height; 16 | } 17 | ``` 18 | 19 | ```html 20 | 21 | 22 | 23 | 24 | 25 | 26 | Document 27 | 28 | 29 | 30 | 31 | 32 | ``` 33 | 34 | ```js 35 | console.log("foo"); 36 | ``` 37 | -------------------------------------------------------------------------------- /tests/assets/pug/_includes/layout.pug: -------------------------------------------------------------------------------- 1 | html.no-js(lang="en") 2 | head 3 | meta(charset='utf-8') 4 | title #{title} 5 | body 6 | block content -------------------------------------------------------------------------------- /tests/assets/pug/_includes/layout2.pug: -------------------------------------------------------------------------------- 1 | html.no-js(lang="en") 2 | head 3 | meta(charset='utf-8') 4 | title #{title} 5 | body!=content 6 | -------------------------------------------------------------------------------- /tests/assets/pug/extends.pug: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pug example 3 | --- 4 | extends /layout.pug 5 | block content 6 | h1 Home 7 | -------------------------------------------------------------------------------- /tests/assets/pug/filter.pug: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown content 3 | url: /article.html 4 | --- 5 | extends ./_includes/layout.pug 6 | block content 7 | :md 8 | # This is a title 9 | 10 | This is a paragraph 11 | 12 | - Option 1 13 | - Option 2 14 | 15 | - const dynamicMd = "# Some dynamic content"; 16 | != filters.md(dynamicMd) 17 | -------------------------------------------------------------------------------- /tests/assets/pug/layout.pug: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pug example 3 | layout: layout2.pug 4 | --- 5 | header 6 | h1 #{title} 7 | -------------------------------------------------------------------------------- /tests/assets/purgecss/_includes/footer.vto: -------------------------------------------------------------------------------- 1 |
    2 | {{ title }} 3 |
    -------------------------------------------------------------------------------- /tests/assets/purgecss/_includes/layout.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ title |> toUpperCase }} 8 | 9 | 10 | {{ content }} 11 | 12 | {{ include "footer.vto" }} 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/assets/purgecss/index.vto: -------------------------------------------------------------------------------- 1 | --- 2 | title: Título 3 | layout: layout.vto 4 | --- 5 | 6 |

    {{ title }}

    7 | 8 | strong 9 | 10 |
    Content
    11 | -------------------------------------------------------------------------------- /tests/assets/purgecss/pages/page1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Page 1 3 | --- 4 | 5 | Content of Page 1 6 | -------------------------------------------------------------------------------- /tests/assets/purgecss/pages/page2.page.js: -------------------------------------------------------------------------------- 1 | export const title = "Page 2"; 2 | 3 | export const url = "/page_2/"; 4 | 5 | export default `
    Content of ${title}
    `; 6 | -------------------------------------------------------------------------------- /tests/assets/purgecss/script.js: -------------------------------------------------------------------------------- 1 | document.querySelector('.dynamic-js') 2 | .addEventListener('click', (event) => { 3 | event.target.classList.add('dynamic-jssub-open'); 4 | }); 5 | -------------------------------------------------------------------------------- /tests/assets/purgecss/static/static.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ title |> toUpperCase }} 8 | 9 | 10 |
    Content
    11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/assets/purgecss/styles.css: -------------------------------------------------------------------------------- 1 | ::root { 2 | --font-family: sans-serif; 3 | } 4 | 5 | body { 6 | font-family: sans-serif; 7 | color: black; 8 | } 9 | 10 | .unused { 11 | margin-top: 30px; 12 | } 13 | 14 | strong { 15 | font-weight: bolder; 16 | } 17 | 18 | em { 19 | font-style: italic; 20 | } 21 | 22 | a[href] { 23 | text-decoration: underline; 24 | } 25 | 26 | html > body .no-such-element .content-vento, html > * .content-vento { 27 | max-width: 800px; 28 | } 29 | 30 | .content-vento:not(.test):focus-visible { 31 | outline: 2px solid blue; 32 | } 33 | 34 | .content-dynamic { 35 | display: flex; 36 | } 37 | 38 | .content-dynamic:focus-visible { 39 | outline: 2px solid lightblue; 40 | } 41 | 42 | .content-static { 43 | margin-top: 25px; 44 | } 45 | 46 | .content-other { 47 | padding: 20px 5px; 48 | } 49 | 50 | .dynamic-js { 51 | opacity: 0; 52 | } 53 | 54 | .dynamic-jssub { 55 | display: none; 56 | } 57 | 58 | .dynamic-jssub-open { 59 | opacity: 1; 60 | } 61 | 62 | #img-option { 63 | max-width: 100%; 64 | } 65 | -------------------------------------------------------------------------------- /tests/assets/reading_info/_includes/layout.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ content }} 4 | 5 |
    6 | Reading time: {{ readingInfo.time }}
    7 | Reading minutes: {{ readingInfo.minutes }}
    8 | Words count: {{ readingInfo.words }}
    9 |
    10 | 11 | -------------------------------------------------------------------------------- /tests/assets/redirects/page1.vto: -------------------------------------------------------------------------------- 1 | --- 2 | url: /page1/ 3 | date: 2025-02-04T20:00Z 4 | oldUrl: /old-page1/ 5 | --- 6 | 7 | Page 1 -------------------------------------------------------------------------------- /tests/assets/redirects/page2.vto: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2025-02-04T20:00Z 3 | oldUrl: 4 | - /old-page2/ 5 | - /other-old-page2/ 307 6 | --- 7 | 8 | Page 2 9 | -------------------------------------------------------------------------------- /tests/assets/relations/_includes/categories.vto: -------------------------------------------------------------------------------- 1 |

    {{ title }}

    2 | 3 |
    {{ content }}
    4 | 5 |

    Posts

    6 |
      7 | {{ for item of it.post }} 8 |
    • {{ item.content |> md }}
    • 9 | {{ /for }} 10 |
    11 | -------------------------------------------------------------------------------- /tests/assets/relations/_includes/comment.vto: -------------------------------------------------------------------------------- 1 |
    {{ content }}
    2 | 3 |

    Post:

    4 | {{ it.post.content |> md }} 5 | -------------------------------------------------------------------------------- /tests/assets/relations/_includes/posts.vto: -------------------------------------------------------------------------------- 1 |
    {{ content }}
    2 | 3 | {{ if it.category }} 4 |

    Category:

    5 | {{ category.title }} 6 | {{ category.content |> md }} 7 | {{ /if }} 8 | 9 |
      10 | {{ for com of it.comment }} 11 |
    • {{ com.content |> md }}
    • 12 | {{ /for }} 13 |
    14 | -------------------------------------------------------------------------------- /tests/assets/relations/categories/_data.yml: -------------------------------------------------------------------------------- 1 | type: category 2 | layout: categories.vto -------------------------------------------------------------------------------- /tests/assets/relations/categories/category-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Category 1 3 | basename: category-1 4 | renderOrder: -2 5 | --- 6 | 7 | This is the first category 8 | -------------------------------------------------------------------------------- /tests/assets/relations/categories/category-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Category 2 3 | basename: category-2 4 | --- 5 | 6 | This is the second category 7 | -------------------------------------------------------------------------------- /tests/assets/relations/comments/_data.yml: -------------------------------------------------------------------------------- 1 | type: comment -------------------------------------------------------------------------------- /tests/assets/relations/comments/comment-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: 1 3 | --- 4 | 5 | This is the first comment 6 | -------------------------------------------------------------------------------- /tests/assets/relations/comments/comment-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: 2 3 | renderOrder: -1 4 | --- 5 | 6 | This is the second comment 7 | -------------------------------------------------------------------------------- /tests/assets/relations/comments/comment-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: 3 3 | --- 4 | 5 | This is the third comment 6 | -------------------------------------------------------------------------------- /tests/assets/relations/posts/_data.yml: -------------------------------------------------------------------------------- 1 | type: post 2 | layout: posts.vto -------------------------------------------------------------------------------- /tests/assets/relations/posts/post-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: first-post 3 | category_id: category-2 4 | comment_id: [1, 3] 5 | renderOrder: 1 6 | --- 7 | 8 | First post with category 2 and comments 1 and 3 9 | -------------------------------------------------------------------------------- /tests/assets/relations/posts/post-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: second-post 3 | category_id: category-1 4 | comment_id: [2] 5 | --- 6 | 7 | Second post with category 1 and comment 2 8 | -------------------------------------------------------------------------------- /tests/assets/relative_urls/about-us/contact.md: -------------------------------------------------------------------------------- 1 | Contact 2 | 3 | - [Index](/) 4 | - [About us](/about-us) 5 | - [Contact](/about-us/contact) 6 | - [Presentation](/about-us/presentation) 7 | -------------------------------------------------------------------------------- /tests/assets/relative_urls/about-us/index.md: -------------------------------------------------------------------------------- 1 | About us 2 | 3 | - [Index](/) 4 | - [About us](/about-us) 5 | - [Contact](/about-us/contact) 6 | - [Presentation](/about-us/presentation) 7 | -------------------------------------------------------------------------------- /tests/assets/relative_urls/about-us/presentation.md: -------------------------------------------------------------------------------- 1 | Presentation 2 | 3 | - [Index](/) 4 | - [About us](/about-us) 5 | - [Contact](/about-us/contact) 6 | - [Presentation](/about-us/presentation) 7 | -------------------------------------------------------------------------------- /tests/assets/relative_urls/index.md: -------------------------------------------------------------------------------- 1 | Index 2 | 3 | - [Index](/) 4 | - [About us](about-us) 5 | - [Contact](./about-us/contact) 6 | - [Presentation](about-us/presentation) 7 | - [Ignored](?ignored=true) 8 | - [Ignored](#ignored) 9 | - [Ignored](https://ignored.com) 10 | - [Ignored](//ignored.com) 11 | -------------------------------------------------------------------------------- /tests/assets/relative_urls/styles.css: -------------------------------------------------------------------------------- 1 | @import "/style.css"; 2 | @import "other-styles.css"; 3 | @import "./other-styles.css"; 4 | @import '/style.css'; 5 | @import 'other-styles.css'; 6 | @import './other-styles.css'; 7 | @import url('/style.css'); 8 | @import url('other-styles.css'); 9 | @import url('./other-styles.css'); 10 | @import url(/style.css); 11 | @import url(other-styles.css); 12 | @import url(./other-styles.css); 13 | 14 | @font-face { 15 | font-family: "foo"; 16 | src: url("/fonts/cicle_fina-webfont.woff2"); 17 | } 18 | 19 | div { 20 | background-image: url("/img/avatar.png"); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tests/assets/remark/basic.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Basic page 3 | --- 4 | 5 | # Normal page 6 | 7 | - List 8 | - Of 9 | - Elements 10 | -------------------------------------------------------------------------------- /tests/assets/remark/empty.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | --- 4 | -------------------------------------------------------------------------------- /tests/assets/remark/with-attributes.md: -------------------------------------------------------------------------------- 1 | [link](#foo){target=_blank} 2 | -------------------------------------------------------------------------------- /tests/assets/remark/with-code.md: -------------------------------------------------------------------------------- 1 | ```html 2 |

    Example

    3 | ``` 4 | 5 | ``` 6 |

    Example without color highlight

    7 | ``` 8 | -------------------------------------------------------------------------------- /tests/assets/remark/with-deflist.md: -------------------------------------------------------------------------------- 1 | 2 | This is a definition title 3 | : And this the description 4 | : Other description 5 | 6 | Other title 7 | : And other description 8 | -------------------------------------------------------------------------------- /tests/assets/remark/with-filter.vto: -------------------------------------------------------------------------------- 1 | --- 2 | title: Module **example** 3 | description: | 4 | Welcome to this [page](/) 5 | --- 6 | 7 |

    {{ title |> md(true) }}

    8 | 9 |
    {{ description |> md }}
    10 | -------------------------------------------------------------------------------- /tests/assets/remark/with-module.page.js: -------------------------------------------------------------------------------- 1 | export const title = "Module example"; 2 | export const templateEngine = "js,md"; 3 | 4 | export default function ({ title }) { 5 | return ` 6 | # ${title} 7 | 8 | [Back to home](/) 9 | `; 10 | } 11 | -------------------------------------------------------------------------------- /tests/assets/remark/with-vto.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Module **example** 3 | templateEngine: vto,md 4 | myData: 5 | one: un 6 | two: dous 7 | three: tres 8 | --- 9 | 10 | # {{ title }} 11 | 12 | Foo 13 | 14 | {{ for title, no of myData }} 15 | - {{ title }}: [{{ no }}](/items/{{ no }}.html) 16 | {{ /for }} 17 | -------------------------------------------------------------------------------- /tests/assets/remote_files/_includes/name.js: -------------------------------------------------------------------------------- 1 | export default "Name"; 2 | -------------------------------------------------------------------------------- /tests/assets/remote_files/_includes/templates/local1.njk: -------------------------------------------------------------------------------- 1 | This is a local template 2 | 3 | {% include "./remote-template2.njk" %} 4 | -------------------------------------------------------------------------------- /tests/assets/remote_files/_remotes/_data.yml: -------------------------------------------------------------------------------- 1 | title: Remote title -------------------------------------------------------------------------------- /tests/assets/remote_files/_remotes/asset.txt: -------------------------------------------------------------------------------- 1 | remote asset -------------------------------------------------------------------------------- /tests/assets/remote_files/_remotes/hello.js: -------------------------------------------------------------------------------- 1 | import name from "./name.js"; 2 | 3 | export default function () { 4 | console.log("hello", name); 5 | } 6 | -------------------------------------------------------------------------------- /tests/assets/remote_files/_remotes/other-remote-style.css: -------------------------------------------------------------------------------- 1 | /* Other remote style */ 2 | body { 3 | color: yellow; 4 | } 5 | -------------------------------------------------------------------------------- /tests/assets/remote_files/_remotes/remote-style.css: -------------------------------------------------------------------------------- 1 | /* Remote style */ 2 | body { 3 | color: red; 4 | } -------------------------------------------------------------------------------- /tests/assets/remote_files/_remotes/remote-template2.njk: -------------------------------------------------------------------------------- 1 | This is a remote template -------------------------------------------------------------------------------- /tests/assets/remote_files/_remotes/remote1.njk: -------------------------------------------------------------------------------- 1 |

    Remote layout

    2 |

    {{ title }}

    3 | 4 | {{ content }} 5 | 6 | {% include "templates/local1.njk" %} -------------------------------------------------------------------------------- /tests/assets/remote_files/_remotes/styles2.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: blue; 3 | } -------------------------------------------------------------------------------- /tests/assets/remote_files/_remotes/variables.scss: -------------------------------------------------------------------------------- 1 | $color: blue; -------------------------------------------------------------------------------- /tests/assets/remote_files/local1.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: remote1.njk 3 | --- 4 | 5 | Hello world 6 | -------------------------------------------------------------------------------- /tests/assets/remote_files/scripts.js: -------------------------------------------------------------------------------- 1 | import hello from "./_includes/hello.js"; 2 | 3 | hello(); 4 | -------------------------------------------------------------------------------- /tests/assets/remote_files/styles1.css: -------------------------------------------------------------------------------- 1 | @import "remote-style.css"; 2 | @import "./other-remote-style.css"; 3 | -------------------------------------------------------------------------------- /tests/assets/remote_files/styles3.scss: -------------------------------------------------------------------------------- 1 | @use "variables" as *; 2 | 3 | body { 4 | color: $color; 5 | } 6 | -------------------------------------------------------------------------------- /tests/assets/render_order/_includes/paginate.js: -------------------------------------------------------------------------------- 1 | export default function ({ pagination }) { 2 | return JSON.stringify(pagination); 3 | } 4 | -------------------------------------------------------------------------------- /tests/assets/render_order/extra-page.md: -------------------------------------------------------------------------------- 1 | Hello world 2 | -------------------------------------------------------------------------------- /tests/assets/render_order/pages.page.ts: -------------------------------------------------------------------------------- 1 | export const url = "/articles/"; 2 | export default function* () { 3 | const pages = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | 5 | for (const page of pages) { 6 | yield { 7 | content: page, 8 | basename: page, 9 | }; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/assets/render_order/pages2.page.ts: -------------------------------------------------------------------------------- 1 | export const renderOrder = 2; 2 | 3 | export default function* () { 4 | const pages = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; 5 | 6 | for (const page of pages) { 7 | yield { 8 | content: page, 9 | url: `/articles/${page}/`, 10 | }; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/assets/render_order/pagination.page.ts: -------------------------------------------------------------------------------- 1 | import type { Data } from "../../../core/file.ts"; 2 | 3 | export const layout = "paginate.js"; 4 | export const renderOrder = 1; 5 | 6 | export default function* ({ search, paginate }: Data) { 7 | const result = search.pages(); 8 | const pages = paginate(result, { 9 | size: 5, 10 | }); 11 | 12 | yield* pages; 13 | } 14 | -------------------------------------------------------------------------------- /tests/assets/resolve_urls/articles/article-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | url: ./first-article/ 3 | --- 4 | 5 | 6 | First article 7 | 8 | [Go to second](./article-2.md) 9 | [Go to third](./article-3.md) 10 | [Go to other](../other.md) 11 | [Go to index](/index.md) -------------------------------------------------------------------------------- /tests/assets/resolve_urls/articles/article-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | url: /drafts/second-article/ 3 | --- 4 | 5 | 6 | Second article 7 | 8 | [Go to first](article-1.md) 9 | [Go to third](article-3.md) 10 | [Go to other](../other.md) 11 | [Go to index](/index.md) -------------------------------------------------------------------------------- /tests/assets/resolve_urls/articles/article-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | url: ./third article/ 3 | --- 4 | 5 | 6 | Third article 7 | 8 | [Go to second](./article-2.md) 9 | [Go to other](../other.md) 10 | [Go to index](/index.md) 11 | -------------------------------------------------------------------------------- /tests/assets/resolve_urls/index.md: -------------------------------------------------------------------------------- 1 | 2 | [url](/bar) 3 | [url](foo) 4 | [url](./foo) 5 | [url](../foo) 6 | [url](#foo) 7 | [url](?foo=bar) 8 | [url](~/other.md) 9 | [url](other.md) 10 | [url](https://domain.com) 11 | [url](other.md?tab=1) 12 | [url](other.md#tab-1) 13 | [url](~/statics/robots.txt) 14 | [url](tílde-and-eñe.md) 15 | [static-file](robots.txt) 16 | [static-file](statics/asset.md) 17 | -------------------------------------------------------------------------------- /tests/assets/resolve_urls/other.md: -------------------------------------------------------------------------------- 1 | Other page 2 | 3 | [See robots.txt](statics/robots.txt) 4 | -------------------------------------------------------------------------------- /tests/assets/resolve_urls/statics/asset.md: -------------------------------------------------------------------------------- 1 | This file is a page. 2 | -------------------------------------------------------------------------------- /tests/assets/resolve_urls/statics/robots.txt: -------------------------------------------------------------------------------- 1 | # Robots TXT -------------------------------------------------------------------------------- /tests/assets/resolve_urls/tílde-and-eñe.md: -------------------------------------------------------------------------------- 1 | Tilde and eñe 2 | -------------------------------------------------------------------------------- /tests/assets/sass/_includes/sass/_list.scss: -------------------------------------------------------------------------------- 1 | @mixin list-reset { 2 | margin: 0; 3 | padding: 0; 4 | list-style: none; 5 | } 6 | -------------------------------------------------------------------------------- /tests/assets/sass/_includes/sass/bootstrap.scss: -------------------------------------------------------------------------------- 1 | @forward "./list"; -------------------------------------------------------------------------------- /tests/assets/sass/_includes/sass/button/index.scss: -------------------------------------------------------------------------------- 1 | @use "../third.scss"; 2 | 3 | $font-stack: Helvetica, sans-serif; 4 | $primary-color: #333; 5 | 6 | body { 7 | font: 100% $font-stack; 8 | color: $primary-color; 9 | } 10 | -------------------------------------------------------------------------------- /tests/assets/sass/_includes/sass/colors.scss: -------------------------------------------------------------------------------- 1 | $primary-color: blue; 2 | $secondary-color: red; -------------------------------------------------------------------------------- /tests/assets/sass/_includes/sass/third.scss: -------------------------------------------------------------------------------- 1 | $font-stack: Helvetica, sans-serif; 2 | 3 | html { 4 | scroll-behavior: smooth; 5 | font: 100% $font-stack; 6 | } 7 | -------------------------------------------------------------------------------- /tests/assets/sass/index.scss: -------------------------------------------------------------------------------- 1 | @use "sass/bootstrap"; 2 | @use "sass/button"; 3 | @use "./_includes/sass/colors"; 4 | @import "./styles.css"; 5 | 6 | li { 7 | @include bootstrap.list-reset; 8 | } 9 | -------------------------------------------------------------------------------- /tests/assets/sass/index2.sass: -------------------------------------------------------------------------------- 1 | @use "sass/colors" as * 2 | 3 | body 4 | color: $primary-color 5 | -------------------------------------------------------------------------------- /tests/assets/sass/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | } -------------------------------------------------------------------------------- /tests/assets/search/a.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A 3 | --- 4 | 5 | Page A -------------------------------------------------------------------------------- /tests/assets/search/b.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | Page B -------------------------------------------------------------------------------- /tests/assets/search/c.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: C 3 | --- 4 | 5 | Page C -------------------------------------------------------------------------------- /tests/assets/search/d.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | Page D -------------------------------------------------------------------------------- /tests/assets/search/index.vto: -------------------------------------------------------------------------------- 1 | --- 2 | title: Z 3 | --- 4 | 5 | {{ for post of search.pages("", "title") }} 6 | {{- post.basename -}} 7 | {{ /for }} 8 | -------------------------------------------------------------------------------- /tests/assets/sheets/_data/attendance_numbers.numbers: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/sheets/_data/attendance_numbers.numbers -------------------------------------------------------------------------------- /tests/assets/sheets/index.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ for key, column of attendance_numbers[0] }} 6 | 7 | {{ /for }} 8 | 9 | 10 | {{ for row of attendance_numbers }} 11 | 12 | {{ for key, column of row }} 13 | 14 | {{ /for }} 15 | 16 | {{ /for }} 17 |
    {{ key }}
    {{ column }}
    18 | 19 | -------------------------------------------------------------------------------- /tests/assets/simple/.page1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: This page should be avoided 3 | --- 4 | 5 | # Welcome 6 | -------------------------------------------------------------------------------- /tests/assets/simple/_page1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: This page should be avoided 3 | --- 4 | 5 | # Welcome 6 | -------------------------------------------------------------------------------- /tests/assets/simple/foo.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Second page 3 | url: /page2.html 4 | --- 5 | 6 | # Welcome 7 | -------------------------------------------------------------------------------- /tests/assets/simple/page1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: First page 3 | --- 4 | 5 | # Welcome 6 | -------------------------------------------------------------------------------- /tests/assets/slugify_urls/Chourizos ao viño.md: -------------------------------------------------------------------------------- 1 | Page 2 2 | -------------------------------------------------------------------------------- /tests/assets/slugify_urls/Page 1.md: -------------------------------------------------------------------------------- 1 | Page 1 2 | -------------------------------------------------------------------------------- /tests/assets/slugify_urls/UI Styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: black; 3 | } -------------------------------------------------------------------------------- /tests/assets/slugify_urls/_headers: -------------------------------------------------------------------------------- 1 | /* 2 | X-Robots-Tag: noindex 3 | -------------------------------------------------------------------------------- /tests/assets/slugify_urls/generator.page.js: -------------------------------------------------------------------------------- 1 | export default function* (_, { slugify }) { 2 | yield { 3 | title: slugify("Filloas con nocilla"), 4 | url: "/Filloas con nocilla/", 5 | content: "Filloas con nocilla", 6 | }; 7 | yield { 8 | title: slugify("/803Ñfon sfodij&%&/(/"), 9 | url: "/803Ñfon sfodij&%&/(/", 10 | content: "Foo", 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /tests/assets/slugify_urls/page-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | url: /Páxina número / tres/ 3 | --- 4 | 5 | Page 3 6 | -------------------------------------------------------------------------------- /tests/assets/slugify_urls/page-4.md: -------------------------------------------------------------------------------- 1 | --- 2 | url: /páxina 4.html 3 | --- 4 | 5 | Page 4 6 | -------------------------------------------------------------------------------- /tests/assets/sri/index.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/assets/sri/script.js: -------------------------------------------------------------------------------- 1 | console.log("hello world"); 2 | -------------------------------------------------------------------------------- /tests/assets/static_files/_headers: -------------------------------------------------------------------------------- 1 | headers -------------------------------------------------------------------------------- /tests/assets/static_files/_static/inner/yes.txt: -------------------------------------------------------------------------------- 1 | yes -------------------------------------------------------------------------------- /tests/assets/static_files/four.no: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/static_files/four.no -------------------------------------------------------------------------------- /tests/assets/static_files/one.yes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/static_files/one.yes -------------------------------------------------------------------------------- /tests/assets/static_files/other/one: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/other/two: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/other2/one: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/other2/two: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/posts/2022-01-01_first-post/assets/inner/inner2/styles.scss: -------------------------------------------------------------------------------- 1 | html { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /tests/assets/static_files/posts/2022-01-01_first-post/assets/inner/styles1.scss: -------------------------------------------------------------------------------- 1 | html { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /tests/assets/static_files/posts/2022-01-01_first-post/assets/styles.scss: -------------------------------------------------------------------------------- 1 | html { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /tests/assets/static_files/posts/2022-01-01_first-post/callback.copy2: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/posts/2022-01-01_first-post/individual-file: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/posts/2022-01-01_first-post/inner/callback1.copy2: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/posts/2022-01-01_first-post/inner/inner2/callback2.copy2: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/posts/2022-01-01_first-post/inner/inner2/to-copy2.copy: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/posts/2022-01-01_first-post/inner/to-copy1.copy: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/posts/2022-01-01_first-post/to-copy.copy: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/script/2022-01-01_app/main.js: -------------------------------------------------------------------------------- 1 | console.log("Hello world!"); 2 | -------------------------------------------------------------------------------- /tests/assets/static_files/static/_no-copy: -------------------------------------------------------------------------------- 1 | no-copy -------------------------------------------------------------------------------- /tests/assets/static_files/static/_redirects: -------------------------------------------------------------------------------- 1 | redirects -------------------------------------------------------------------------------- /tests/assets/static_files/static/one.yes: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/static/two.yes: -------------------------------------------------------------------------------- 1 | content -------------------------------------------------------------------------------- /tests/assets/static_files/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: blue; 3 | } -------------------------------------------------------------------------------- /tests/assets/static_files/styles/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /tests/assets/static_files/three.no: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/static_files/three.no -------------------------------------------------------------------------------- /tests/assets/static_files/two.yes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/static_files/two.yes -------------------------------------------------------------------------------- /tests/assets/symlinks/footer_link.njk: -------------------------------------------------------------------------------- 1 |
    2 | End 3 |
    -------------------------------------------------------------------------------- /tests/assets/symlinks/footer_link_link: -------------------------------------------------------------------------------- 1 | footer_link.njk -------------------------------------------------------------------------------- /tests/assets/symlinks/includes_link/footer.njk: -------------------------------------------------------------------------------- 1 |
    2 | End 3 |
    -------------------------------------------------------------------------------- /tests/assets/symlinks/includes_link/layouts/layout.njk: -------------------------------------------------------------------------------- 1 | ../../layout_link.njk -------------------------------------------------------------------------------- /tests/assets/symlinks/index_link.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "layouts/layout.njk" 3 | title: Hello world 4 | --- 5 | 6 | Hello wold 7 | -------------------------------------------------------------------------------- /tests/assets/symlinks/layout_link.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 |

    {{ title }}

    4 | 5 | 6 | {{ content | safe }} 7 | 8 | {% include "footer.njk" %} 9 | 10 | -------------------------------------------------------------------------------- /tests/assets/symlinks/src/_includes: -------------------------------------------------------------------------------- 1 | ../includes_link/ -------------------------------------------------------------------------------- /tests/assets/symlinks/src/index.md: -------------------------------------------------------------------------------- 1 | ../index_link.md -------------------------------------------------------------------------------- /tests/assets/tailwindcss/script.js: -------------------------------------------------------------------------------- 1 | menuButton.addEventListener("click", function () { 2 | const classList = document.getElementById("nav").classList; 3 | classList.toggle("gap-8"); 4 | classList.toggle("gap-16"); 5 | }); 6 | -------------------------------------------------------------------------------- /tests/assets/tailwindcss/styles.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | -------------------------------------------------------------------------------- /tests/assets/terser/main.js: -------------------------------------------------------------------------------- 1 | import { one, two } from "./numbers.js"; 2 | 3 | console.log(one() + two()); 4 | -------------------------------------------------------------------------------- /tests/assets/terser/numbers.vto: -------------------------------------------------------------------------------- 1 | --- 2 | url: /numbers.js 3 | one: 1 4 | two: 2 5 | --- 6 | 7 | export function one () { 8 | return {{ one }}; 9 | } 10 | 11 | export function two () { 12 | return {{ two }}; 13 | } 14 | /* 15 | Commented code 16 | export function two () { 17 | return {{ two }}; 18 | } 19 | */ -------------------------------------------------------------------------------- /tests/assets/toml/_data.toml: -------------------------------------------------------------------------------- 1 | [site] 2 | title = "Default title" 3 | description = "Default description" 4 | -------------------------------------------------------------------------------- /tests/assets/toml/_includes/layout.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ title }} 4 | 5 | 6 |

    {{ site.title }}

    7 |

    {{ site.description }}

    8 |

    {{ title }}

    9 |

    {{ content }}

    10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/assets/toml/index.page.toml: -------------------------------------------------------------------------------- 1 | title = "Title of the index" 2 | content = "Content of the index" 3 | date = 1979-06-21T23:45:00.000Z 4 | layout = "layout.vto" -------------------------------------------------------------------------------- /tests/assets/toml/page1.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'First page' 3 | +++ 4 | 5 | # Welcome 6 | -------------------------------------------------------------------------------- /tests/assets/transform_images/_data.yml: -------------------------------------------------------------------------------- 1 | transformImages: 2 | - suffix: -small 3 | resize: 20 4 | blur: 10 5 | - suffix: -big 6 | resize: 20 7 | blur: 10 8 | matches: \.png$ 9 | - format: 10 | - format: jpg 11 | progressive: true 12 | - webp 13 | - format: png 14 | - format: jpg 15 | suffix: -never 16 | matches: \.jpg$ 17 | -------------------------------------------------------------------------------- /tests/assets/transform_images/lume.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumeland/lume/30dc98eb08406cb16310e1931d936a31eab6f65c/tests/assets/transform_images/lume.PNG -------------------------------------------------------------------------------- /tests/assets/unocss/styles.css: -------------------------------------------------------------------------------- 1 | /* unocss-placeholder */ 2 | 3 | /* https://unocss.dev/transformers/directives#usage */ 4 | .custom-div { 5 | @apply text-center my-0 font-medium; 6 | --at-apply: "hover:text-red"; 7 | } 8 | 9 | .grid { 10 | --uno: grid grid-cols-2; 11 | } 12 | 13 | @screen sm { 14 | .grid { 15 | --uno: grid-cols-3; 16 | } 17 | } 18 | 19 | .btn-blue { 20 | background-color: theme("colors.blue.500"); 21 | } 22 | 23 | /* https://unocss.dev/transformers/variant-group#usage */ 24 | .variant-group { 25 | @apply hover:(bg-gray-400 font-medium) font-(light mono); 26 | } 27 | -------------------------------------------------------------------------------- /tests/assets/url/default-filter.page.js: -------------------------------------------------------------------------------- 1 | export const title = "Default Filter"; 2 | export const url = "/default-filter/"; 3 | 4 | export default function (_, { url, htmlUrl }) { 5 | return ` 6 | 7 | 8 | 9 | Default Filter 10 | 11 | 12 | Url 13 | ${htmlUrl?.('htmlUrl', true)} 14 | 17 | 18 | 19 | `; 20 | } 21 | -------------------------------------------------------------------------------- /tests/assets/url/renamed-filter.page.js: -------------------------------------------------------------------------------- 1 | export const title = "Renamed Filter"; 2 | export const url = "/renamed-filter/"; 3 | 4 | export default function (_, { urlify, htmlUrlify }) { 5 | return ` 6 | 7 | 8 | 9 | Renamed Filter 10 | 11 | 12 | Urlify 13 | ${ 14 | htmlUrlify?.( 15 | 'htmlUrlify', 16 | true, 17 | ) 18 | } 19 | 20 | 21 | `; 22 | } 23 | -------------------------------------------------------------------------------- /tests/assets/vento/_components/Button.vto: -------------------------------------------------------------------------------- 1 | {{ content }} 2 | -------------------------------------------------------------------------------- /tests/assets/vento/_components/container.vto: -------------------------------------------------------------------------------- 1 |
    {{ content }}
    -------------------------------------------------------------------------------- /tests/assets/vento/_includes/footer.vto: -------------------------------------------------------------------------------- 1 |
    2 | {{ title }} 3 |
    -------------------------------------------------------------------------------- /tests/assets/vento/_includes/layout.vto: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ title |> toUpperCase }} 8 | 9 | 10 | {{ content }} 11 | 12 | {{ include "footer.vto" }} 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/assets/vento/index.vto: -------------------------------------------------------------------------------- 1 | --- 2 | title: Título 3 | layout: layout.vto 4 | --- 5 | 6 |

    {{ title }}

    7 | 8 | {{ comp button { 9 | href: "https://lume.land" 10 | } }} 11 | Go to Lume 12 | {{ /comp }} 13 | 14 | {{ set href = "/" |> url(true) }} 15 | 16 | {{ comp container }} 17 | {{ comp button {href, content: "Go to Lume"} /}} 18 | {{ /comp }} 19 | 20 | {{ comp.button({href, content: "Go to Lume"}) |> toUpperCase }} 21 | 22 | {{ "custom filter" |> upper }} 23 | 24 | {{ "title" |> fromPage }} 25 | {{ "title" |> await fromPageAsync }} 26 | -------------------------------------------------------------------------------- /tests/assets/vento/vto-filter.page.js: -------------------------------------------------------------------------------- 1 | export const title = "vento filter example"; 2 | 3 | export default function (data, { vto }) { 4 | const content = `

    {{ title |> toUpperCase }}

    `; 5 | return vto(content, data); 6 | } 7 | -------------------------------------------------------------------------------- /tests/assets/well_known/.well-known/page.vto: -------------------------------------------------------------------------------- 1 | --- 2 | url: ./security.txt 3 | --- 4 | 5 | Content. -------------------------------------------------------------------------------- /tests/base_path.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import basePath from "../plugins/base_path.ts"; 3 | 4 | Deno.test("base_path plugin", async (t) => { 5 | const site = getSite({ 6 | src: "base_path", 7 | location: new URL("https://example.com/blog"), 8 | }); 9 | 10 | site.use(basePath()); 11 | site.add([".css"]); 12 | 13 | await build(site); 14 | await assertSiteSnapshot(t, site); 15 | }); 16 | -------------------------------------------------------------------------------- /tests/brotli.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import brotli from "../plugins/brotli.ts"; 3 | import extractDate from "../plugins/extract_date.ts"; 4 | 5 | Deno.test("brotli plugin", async (t) => { 6 | const site = getSite({ 7 | src: "normal", 8 | }); 9 | 10 | site.add([".png", ".css", ".json"]); 11 | 12 | site.use(brotli()); 13 | site.use(extractDate()); 14 | 15 | await build(site); 16 | await assertSiteSnapshot(t, site); 17 | }); 18 | 19 | Deno.test("brotli plugin with options", async (t) => { 20 | const site = getSite({ 21 | src: "normal", 22 | }); 23 | 24 | site.add([".png", ".css", ".json"]); 25 | 26 | site.use(brotli({ 27 | extensions: [".css"], 28 | quality: 1, 29 | })); 30 | site.use(extractDate()); 31 | 32 | await build(site); 33 | await assertSiteSnapshot(t, site); 34 | }); 35 | -------------------------------------------------------------------------------- /tests/cache_busting.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResponseSnapshot, getServer } from "./utils.ts"; 2 | import cacheBusting from "../middlewares/cache_busting.ts"; 3 | 4 | Deno.test("cache busting middleware", async (t) => { 5 | const server = getServer((request) => { 6 | return new Response(`${request.url}`); 7 | }); 8 | 9 | server.use(cacheBusting()); 10 | 11 | await assertResponseSnapshot( 12 | t, 13 | server, 14 | new Request("http://localhost/v1/styles.css"), 15 | ); 16 | await assertResponseSnapshot( 17 | t, 18 | server, 19 | new Request("http://localhost/v2873/scripts.js"), 20 | ); 21 | }); 22 | -------------------------------------------------------------------------------- /tests/code_highlight.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import codeHighlight from "../plugins/code_highlight.ts"; 3 | 4 | Deno.test("code_hightlight plugin with default css file", async (t) => { 5 | const site = getSite({ 6 | src: "code_highlight", 7 | }); 8 | 9 | site.use(codeHighlight({ 10 | theme: { 11 | name: "a11y-dark", 12 | }, 13 | })); 14 | 15 | await build(site); 16 | await assertSiteSnapshot(t, site); 17 | }); 18 | 19 | Deno.test("code_hightlight plugin", async (t) => { 20 | const site = getSite({ 21 | src: "code_highlight", 22 | }); 23 | 24 | site.use(codeHighlight({ 25 | theme: { 26 | name: "a11y-dark", 27 | cssFile: "theme.css", 28 | }, 29 | })); 30 | 31 | await build(site); 32 | await assertSiteSnapshot(t, site); 33 | }); 34 | -------------------------------------------------------------------------------- /tests/core/loaders_assets/1_page.txt: -------------------------------------------------------------------------------- 1 | Content -------------------------------------------------------------------------------- /tests/core/loaders_assets/2021-12-19-20-35_page.txt: -------------------------------------------------------------------------------- 1 | Content -------------------------------------------------------------------------------- /tests/core/loaders_assets/2021-12-19_page.txt: -------------------------------------------------------------------------------- 1 | Content -------------------------------------------------------------------------------- /tests/core/loaders_assets/_data/value1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Value1" 3 | } 4 | -------------------------------------------------------------------------------- /tests/core/loaders_assets/_data/value2.yml: -------------------------------------------------------------------------------- 1 | name: Value2 2 | -------------------------------------------------------------------------------- /tests/core/loaders_assets/_data/value3/subvalue.ts: -------------------------------------------------------------------------------- 1 | export const name = "Subvalue3"; 2 | -------------------------------------------------------------------------------- /tests/core/loaders_assets/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Title from json", 3 | "tags": ["tag1", "tag2"] 4 | } 5 | -------------------------------------------------------------------------------- /tests/core/loaders_assets/data.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: "Title from default", 3 | tags: "tag1", 4 | }; 5 | 6 | export const subtitle = "Subtitle value"; 7 | -------------------------------------------------------------------------------- /tests/core/loaders_assets/data.txt: -------------------------------------------------------------------------------- 1 | --- 2 | title: Title in the front matter 3 | tags: tag1 4 | --- 5 | Hello world -------------------------------------------------------------------------------- /tests/core/loaders_assets/data.yml: -------------------------------------------------------------------------------- 1 | title: Hello world 2 | tags: 3 | - tag1 4 | - tag2 5 | -------------------------------------------------------------------------------- /tests/core/loaders_assets/page.txt: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2022-05-21 3 | --- 4 | Content -------------------------------------------------------------------------------- /tests/core/utils/data_values.test.ts: -------------------------------------------------------------------------------- 1 | import { assertStrictEquals as equals } from "../../../deps/assert.ts"; 2 | import { getDataValue } from "../../../core/utils/data_values.ts"; 3 | import { build, getSite } from "../../utils.ts"; 4 | import metas from "../../../plugins/metas.ts"; 5 | 6 | Deno.test("Test getDataValue() function", async (t) => { 7 | const site = getSite({ src: "metas" }); 8 | 9 | site.use(metas()); 10 | site.process([".html"], async (pages) => { 11 | for (const page of pages) { 12 | const { data } = page; 13 | if (!data.cover) continue; 14 | 15 | await t.step( 16 | "Data query: =", 17 | () => equals(getDataValue(data, data.metas.image), data.cover), 18 | ); 19 | 20 | await t.step( 21 | "CSS query: $", 22 | () => 23 | equals( 24 | getDataValue(data, '$meta[property="og:image"] attr(content)'), 25 | new URL(site.url(data.cover), site.url(page.data.url, true)).href, 26 | ), 27 | ); 28 | } 29 | }); 30 | 31 | await build(site); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/decap_cms.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import decapCMS from "../plugins/decap_cms.ts"; 3 | 4 | Deno.test("Decap CMS plugin", async (t) => { 5 | const site = getSite({ 6 | src: "decap_cms", 7 | location: new URL("https://example.com"), 8 | }); 9 | 10 | site.use(decapCMS({ 11 | local: false, 12 | identity: "netlify", 13 | })); 14 | 15 | await build(site); 16 | await assertSiteSnapshot(t, site); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/eta.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import eta from "../plugins/eta.ts"; 3 | 4 | Deno.test("build a site with eta", async (t) => { 5 | const site = getSite({ 6 | src: "eta", 7 | location: new URL("https://example.com/blog"), 8 | }); 9 | 10 | site.use(eta()); 11 | 12 | await build(site); 13 | await assertSiteSnapshot(t, site); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/extract_date.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import extractDate from "../plugins/extract_date.ts"; 3 | 4 | Deno.test("extract_date plugin", async (t) => { 5 | const site = getSite({ 6 | src: "normal", 7 | }); 8 | 9 | site.use(extractDate()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/favicon.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import svgo from "../plugins/svgo.ts"; 3 | import favicon from "../plugins/favicon.ts"; 4 | 5 | Deno.test("favicon plugin", async (t) => { 6 | const site = getSite({ 7 | src: "favicon", 8 | }); 9 | 10 | site.use(svgo()); 11 | site.use(favicon()); 12 | 13 | await build(site); 14 | await assertSiteSnapshot(t, site); 15 | }); 16 | -------------------------------------------------------------------------------- /tests/filter_pages.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import filter_pages from "../plugins/filter_pages.ts"; 3 | 4 | Deno.test("Filter pages (allow only /multiple/*)", async (t) => { 5 | const site = getSite({ 6 | src: "module", 7 | }); 8 | 9 | site.use(filter_pages({ 10 | fn(page) { 11 | return page.data.url.startsWith("/multiple/"); 12 | }, 13 | })); 14 | 15 | await build(site); 16 | await assertSiteSnapshot(t, site); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/gzip.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import gzip from "../plugins/gzip.ts"; 3 | import unocss from "../plugins/unocss.ts"; 4 | import extractDate from "../plugins/extract_date.ts"; 5 | 6 | Deno.test("gzip plugin", async (t) => { 7 | const site = getSite({ 8 | src: "normal", 9 | }); 10 | 11 | site.add([".png", ".css", ".json"]); 12 | 13 | site.use(gzip()); 14 | site.use(extractDate()); 15 | 16 | await build(site); 17 | await assertSiteSnapshot(t, site); 18 | }); 19 | 20 | Deno.test("gzip plugin with options", async (t) => { 21 | const site = getSite({ 22 | src: "unocss", 23 | }); 24 | 25 | site.add([".css"]); 26 | site.use(unocss({ 27 | cssFile: "styles.css", 28 | })); 29 | site.use(gzip({ 30 | extensions: [".css"], 31 | })); 32 | site.use(extractDate()); 33 | 34 | await build(site); 35 | await assertSiteSnapshot(t, site); 36 | }); 37 | -------------------------------------------------------------------------------- /tests/icons.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import icons from "../plugins/icons.ts"; 3 | 4 | Deno.test("icons plugin", async (t) => { 5 | const site = getSite({ 6 | src: "icons", 7 | }); 8 | 9 | site.use(icons()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/inline.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import inline from "../plugins/inline.ts"; 3 | 4 | Deno.test("inline plugin", async (t) => { 5 | const site = getSite({ 6 | src: "inline", 7 | }); 8 | 9 | site.use(inline({ 10 | copyAttributes: ["custom", /^data-/, /^@/], 11 | })); 12 | 13 | site.add([".svg", ".js", ".png"]); 14 | site.add("favicon.png", "favicon2.png"); 15 | 16 | await build(site); 17 | await assertSiteSnapshot(t, site); 18 | }); 19 | -------------------------------------------------------------------------------- /tests/json.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | 3 | Deno.test("JSON plugin", async (t) => { 4 | const site = getSite({ 5 | src: "json", 6 | }); 7 | 8 | await build(site); 9 | await assertSiteSnapshot(t, site); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/json_ld.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import jsonLd from "../plugins/json_ld.ts"; 3 | 4 | Deno.test("json_ld plugin", async (t) => { 5 | const site = getSite({ 6 | src: "json_ld", 7 | }); 8 | 9 | site.use(jsonLd()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/jsx.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import jsx from "../plugins/jsx.ts"; 3 | 4 | Deno.test("build a site with jsx/tsx modules", async (t) => { 5 | const site = getSite({ 6 | src: "jsx", 7 | location: new URL("https://example.com/blog"), 8 | }); 9 | 10 | site.use(jsx()); 11 | 12 | await build(site); 13 | await assertSiteSnapshot(t, site); 14 | }); 15 | 16 | Deno.test("Previous preact test with SSX", async (t) => { 17 | const site = getSite({ 18 | src: "jsx_preact", 19 | location: new URL("https://example.com/blog"), 20 | }); 21 | 22 | site.use(jsx()); 23 | 24 | await build(site); 25 | await assertSiteSnapshot(t, site); 26 | }); 27 | -------------------------------------------------------------------------------- /tests/katex.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import katex from "../plugins/katex.ts"; 3 | 4 | Deno.test("Katex plugin", async (t) => { 5 | const site = getSite({ 6 | src: "katex", 7 | }); 8 | 9 | site.use(katex({ 10 | options: { 11 | macros: { 12 | "\\f": "#1f(#2)", 13 | }, 14 | }, 15 | })); 16 | 17 | await build(site); 18 | await assertSiteSnapshot(t, site); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/layout.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | 3 | Deno.test("Layouts", async (t) => { 4 | const site = getSite({ 5 | src: "layouts", 6 | }); 7 | 8 | site.add([".css"]); 9 | 10 | await build(site); 11 | await assertSiteSnapshot(t, site); 12 | }); 13 | -------------------------------------------------------------------------------- /tests/lightningcss.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import lightningcss from "../plugins/lightningcss.ts"; 3 | 4 | Deno.test("Lightningcss plugin (transform)", async (t) => { 5 | const site = getSite({ 6 | src: "lightningcss", 7 | }); 8 | 9 | site.add("index.css"); 10 | site.add("text.css"); 11 | site.use(lightningcss({ 12 | includes: false, 13 | })); 14 | 15 | await build(site); 16 | await assertSiteSnapshot(t, site); 17 | }); 18 | 19 | Deno.test("Lightningcss plugin (bundle)", async (t) => { 20 | const site = getSite({ 21 | src: "lightningcss", 22 | }); 23 | 24 | site.add([".css"]); 25 | site.use(lightningcss()); 26 | 27 | await build(site); 28 | await assertSiteSnapshot(t, site); 29 | }); 30 | -------------------------------------------------------------------------------- /tests/markdown.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import footnotePlugin from "npm:markdown-it-footnote@3.0.3"; 3 | 4 | Deno.test("Build a markdown site", async (t) => { 5 | const site = getSite({ 6 | src: "markdown", 7 | location: new URL("https://example.com/blog"), 8 | }, { 9 | markdown: { 10 | plugins: [footnotePlugin], 11 | keepDefaultPlugins: true, 12 | rules: { 13 | footnote_block_open: () => ( 14 | '

    Footnotes

    \n' + 15 | '
    \n' + 16 | '
      \n' 17 | ), 18 | }, 19 | }, 20 | }); 21 | 22 | await build(site); 23 | await assertSiteSnapshot(t, site); 24 | }); 25 | 26 | Deno.test("Build a markdown with hooks", async (t) => { 27 | const site = getSite({ 28 | src: "markdown", 29 | location: new URL("https://example.com/blog"), 30 | }); 31 | 32 | site.hooks.addMarkdownItPlugin(footnotePlugin); 33 | site.hooks.addMarkdownItRule("footnote_block_open", () => ( 34 | '

      Footnotes

      \n' + 35 | '
      \n' + 36 | '
        \n' 37 | )); 38 | 39 | await build(site); 40 | await assertSiteSnapshot(t, site); 41 | }); 42 | -------------------------------------------------------------------------------- /tests/mdx.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import mdx from "../plugins/mdx.ts"; 3 | import jsx from "../plugins/jsx.ts"; 4 | 5 | Deno.test("Build a mdx site", async (t) => { 6 | const site = getSite({ 7 | src: "mdx", 8 | }); 9 | 10 | site.use(jsx()); 11 | site.use(mdx()); 12 | 13 | await build(site); 14 | await assertSiteSnapshot(t, site); 15 | }); 16 | -------------------------------------------------------------------------------- /tests/metas.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import metas from "../plugins/metas.ts"; 3 | 4 | import type { Page } from "../core/file.ts"; 5 | 6 | Deno.test("metas plugin", async (t) => { 7 | const site = getSite({ 8 | src: "metas", 9 | }); 10 | 11 | site.use(metas()); 12 | site.preprocess([".md"], (pages: Page[]) => { 13 | pages.forEach((page) => { 14 | page.data.excerpt ??= 15 | (page.data.content as string).split("")[0]; 16 | }); 17 | }); 18 | 19 | await build(site); 20 | await assertSiteSnapshot(t, site); 21 | }); 22 | -------------------------------------------------------------------------------- /tests/minify_html.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import minifyHtml from "../plugins/minify_html.ts"; 3 | 4 | Deno.test("minify_html plugin", async (t) => { 5 | const site = getSite({ 6 | src: "minify_html", 7 | }); 8 | 9 | site.use(minifyHtml()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/module.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | 3 | Deno.test("Build a site with js/ts modules", async (t) => { 4 | const site = getSite({ 5 | src: "module", 6 | location: new URL("https://example.com/blog"), 7 | }); 8 | 9 | await build(site); 10 | await assertSiteSnapshot(t, site); 11 | }); 12 | -------------------------------------------------------------------------------- /tests/multilanguage.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import multilanguage from "../plugins/multilanguage.ts"; 3 | 4 | Deno.test("multilanguage plugin", async (t) => { 5 | const site = getSite({ 6 | src: "multilanguage", 7 | }); 8 | 9 | site.use(multilanguage({ 10 | defaultLanguage: "gl", 11 | languages: ["en", "fr", "it", "gl"], 12 | })); 13 | 14 | await build(site); 15 | await assertSiteSnapshot(t, site); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/nav.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import nav from "../plugins/nav.ts"; 3 | 4 | Deno.test("nav plugin", async (t) => { 5 | const site = getSite({ 6 | src: "nav", 7 | }); 8 | 9 | site.use(nav({ order: "order basename" })); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | 15 | Deno.test("nav plugin with pretty urls disabled", async (t) => { 16 | const site = getSite({ 17 | src: "nav", 18 | prettyUrls: false, 19 | }); 20 | 21 | site.use(nav({ order: "order basename" })); 22 | 23 | await build(site); 24 | await assertSiteSnapshot(t, site); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/no_cache.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResponseSnapshot, getServer } from "./utils.ts"; 2 | import noCache from "../middlewares/no_cache.ts"; 3 | 4 | Deno.test("no cache middleware", async (t) => { 5 | const server = getServer(() => { 6 | return new Response("Hello World", { 7 | headers: { 8 | "last-modified": "Mon, 01 Jan 2000 00:00:00 GMT", 9 | etag: "123", 10 | }, 11 | }); 12 | }); 13 | 14 | server.use(noCache()); 15 | 16 | await assertResponseSnapshot(t, server); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/no_cors.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResponseSnapshot, getServer } from "./utils.ts"; 2 | import noCors from "../middlewares/no_cors.ts"; 3 | 4 | Deno.test("no_cors middleware", async (t) => { 5 | const server = getServer(); 6 | 7 | server.use(noCors()); 8 | 9 | await assertResponseSnapshot(t, server); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/og_images.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import ogImages from "../plugins/og_images.ts"; 3 | 4 | Deno.test("OpenGraph images", async (t) => { 5 | const site = getSite({ 6 | src: "og_images", 7 | }); 8 | 9 | site.use(ogImages()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/pagefind.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import pagefind from "../plugins/pagefind.ts"; 3 | 4 | Deno.test( 5 | "Pagefind plugin", 6 | { ignore: Deno.build.os !== "darwin" }, 7 | async (t) => { 8 | const site = getSite({ 9 | src: "pagefind", 10 | }); 11 | 12 | site.use(pagefind()); 13 | 14 | await build(site); 15 | await assertSiteSnapshot(t, site, { 16 | avoidBinaryFilesLength: true, 17 | }); 18 | }, 19 | ); 20 | 21 | Deno.test( 22 | "Pagefind plugin with global variable", 23 | { ignore: Deno.build.os !== "darwin" }, 24 | async (t) => { 25 | const site = getSite({ 26 | src: "pagefind", 27 | }); 28 | 29 | site.use(pagefind({ 30 | ui: { 31 | globalVariable: "pagefind", 32 | }, 33 | })); 34 | 35 | await build(site); 36 | await assertSiteSnapshot(t, site, { 37 | avoidBinaryFilesLength: true, 38 | }); 39 | }, 40 | ); 41 | -------------------------------------------------------------------------------- /tests/pagination.test.ts: -------------------------------------------------------------------------------- 1 | import { assertStrictEquals as equals } from "../deps/assert.ts"; 2 | import { createPaginator } from "../plugins/paginate.ts"; 3 | 4 | Deno.test("pagination plugin function", () => { 5 | const paginator = createPaginator({ 6 | size: 10, 7 | url: (num) => `/page/${num}`, 8 | }); 9 | 10 | const all = Array(90).fill(0).map((_, i) => i + 1); 11 | const pages = paginator(all, { 12 | each(data) { 13 | data.title = `Page ${data.pagination.page}`; 14 | }, 15 | }); 16 | 17 | equals(pages.length, 9); 18 | equals(pages[0].url, "/page/1"); 19 | equals(pages[0].title, "Page 1"); 20 | equals(pages[0].results.length, 10); 21 | equals(pages[0].results[9], 10); 22 | equals(pages[0].pagination.page, 1); 23 | equals(pages[0].pagination.totalPages, 9); 24 | equals(pages[0].pagination.totalResults, 90); 25 | equals(pages[0].pagination.previous, null); 26 | equals(pages[0].pagination.next, "/page/2"); 27 | equals(pages[8].title, "Page 9"); 28 | equals(pages[8].pagination.previous, "/page/8"); 29 | equals(pages[8].pagination.next, null); 30 | }); 31 | -------------------------------------------------------------------------------- /tests/picture.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import transformImages from "../plugins/transform_images.ts"; 3 | import picture from "../plugins/picture.ts"; 4 | 5 | Deno.test("picture plugin", async (t) => { 6 | const site = getSite({ 7 | src: "picture", 8 | }); 9 | 10 | site.use(picture()); 11 | site.use(transformImages()); 12 | 13 | await build(site); 14 | await assertSiteSnapshot(t, site, { 15 | avoidBinaryFilesLength: true, 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/plaintext.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import plaintext from "../plugins/plaintext.ts"; 3 | 4 | Deno.test("Plain text filter", async (t) => { 5 | const site = getSite({ 6 | src: "plaintext", 7 | }); 8 | 9 | site.use(plaintext()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/pretty_urls.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | 3 | Deno.test("Disabled pretty URLs", async (t) => { 4 | const site = getSite({ 5 | src: "simple", 6 | prettyUrls: false, 7 | }); 8 | 9 | await build(site); 10 | await assertSiteSnapshot(t, site); 11 | }); 12 | -------------------------------------------------------------------------------- /tests/prism.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import prism from "../plugins/prism.ts"; 3 | import "npm:prismjs@1.29.0/components/prism-less.js"; 4 | 5 | Deno.test("Prism plugin with default css file", async (t) => { 6 | const site = getSite({ 7 | src: "prism", 8 | }); 9 | 10 | site.use(prism({ 11 | theme: { 12 | name: "dark", 13 | }, 14 | })); 15 | 16 | await build(site); 17 | await assertSiteSnapshot(t, site); 18 | }); 19 | 20 | Deno.test("Prism plugin", async (t) => { 21 | const site = getSite({ 22 | src: "prism", 23 | }); 24 | 25 | site.use(prism({ 26 | theme: { 27 | name: "dark", 28 | cssFile: "theme.css", 29 | }, 30 | })); 31 | 32 | await build(site); 33 | await assertSiteSnapshot(t, site); 34 | }); 35 | -------------------------------------------------------------------------------- /tests/pug.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import pug from "../plugins/pug.ts"; 3 | 4 | Deno.test("build a site with pug", async (t) => { 5 | const site = getSite({ 6 | src: "pug", 7 | location: new URL("https://example.com/blog"), 8 | }); 9 | 10 | site.use(pug()); 11 | 12 | await build(site); 13 | await assertSiteSnapshot(t, site); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/purgecss.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getPath, getSite } from "./utils.ts"; 2 | import { normalizePath } from "../core/utils/path.ts"; 3 | import purgecss from "../plugins/purgecss.ts"; 4 | 5 | Deno.test("purgecss plugin", async (t) => { 6 | const site = getSite({ 7 | src: "purgecss", 8 | }); 9 | 10 | site.add([".css", ".js"]); 11 | site.add("static", "."); 12 | 13 | site.use(purgecss()); 14 | 15 | await build(site); 16 | await assertSiteSnapshot(t, site); 17 | }); 18 | 19 | Deno.test("purgecss plugin with options", async (t) => { 20 | const site = getSite({ 21 | src: "purgecss", 22 | }); 23 | 24 | site.add([".css", ".js"]); 25 | site.add("static", "."); 26 | 27 | site.use(purgecss({ 28 | options: { 29 | content: [ 30 | normalizePath(getPath("./assets/purgecss/static/**/*.html")), 31 | { 32 | raw: '', 33 | extension: "html", 34 | }, 35 | ], 36 | safelist: ["unused"], 37 | blocklist: ["strong"], 38 | variables: true, 39 | }, 40 | })); 41 | 42 | await build(site); 43 | await assertSiteSnapshot(t, site); 44 | }); 45 | -------------------------------------------------------------------------------- /tests/reading_info.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import readingInfo from "../plugins/reading_info.ts"; 3 | 4 | Deno.test("Reading info plugin", async (t) => { 5 | const site = getSite({ 6 | src: "reading_info", 7 | }); 8 | 9 | site.use(readingInfo()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/redirects.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import redirects from "../plugins/redirects.ts"; 3 | 4 | Deno.test("redirects plugin", async (t) => { 5 | const site = getSite({ 6 | src: "redirects", 7 | }); 8 | 9 | site.use(redirects()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | 15 | Deno.test("redirects plugin for netlify", async (t) => { 16 | const site = getSite({ 17 | src: "redirects", 18 | }); 19 | 20 | site.use(redirects({ 21 | output: "netlify", 22 | })); 23 | 24 | await build(site); 25 | await assertSiteSnapshot(t, site); 26 | }); 27 | 28 | Deno.test("redirects plugin for vercel", async (t) => { 29 | const site = getSite({ 30 | src: "redirects", 31 | }); 32 | 33 | site.use(redirects({ 34 | output: "vercel", 35 | })); 36 | 37 | await build(site); 38 | await assertSiteSnapshot(t, site); 39 | }); 40 | 41 | Deno.test("redirects plugin for json", async (t) => { 42 | const site = getSite({ 43 | src: "redirects", 44 | }); 45 | 46 | site.use(redirects({ 47 | output: "json", 48 | })); 49 | 50 | await build(site); 51 | await assertSiteSnapshot(t, site); 52 | }); 53 | -------------------------------------------------------------------------------- /tests/relations.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import relations from "../plugins/relations.ts"; 3 | 4 | Deno.test("relations plugin", async (t) => { 5 | const site = getSite({ 6 | src: "relations", 7 | }); 8 | 9 | site.use(relations({ 10 | foreignKeys: { 11 | post: "post_id", 12 | category: { foreignKey: "category_id", idKey: "basename" }, 13 | comment: { foreignKey: "comment_id", pluralRelationKey: "comments" }, 14 | }, 15 | })); 16 | 17 | await build(site); 18 | await assertSiteSnapshot(t, site); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/relative_urls.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import relativeUrls from "../plugins/relative_urls.ts"; 3 | 4 | Deno.test("relative_url plugin", async (t) => { 5 | const site = getSite({ 6 | src: "relative_urls", 7 | location: new URL("https://example.com/blog"), 8 | }); 9 | 10 | site.use(relativeUrls()); 11 | site.add([".css"]); 12 | 13 | await build(site); 14 | await assertSiteSnapshot(t, site); 15 | }); 16 | 17 | Deno.test("relative_url plugin when pretty urls disabled", async (t) => { 18 | const site = getSite({ 19 | src: "relative_urls", 20 | location: new URL("https://example.com/blog"), 21 | prettyUrls: false, 22 | }); 23 | 24 | site.use(relativeUrls()); 25 | 26 | await build(site); 27 | await assertSiteSnapshot(t, site); 28 | }); 29 | -------------------------------------------------------------------------------- /tests/remark.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import remark from "../plugins/remark.ts"; 3 | 4 | Deno.test("Build a markdown site", async (t) => { 5 | const site = getSite({ 6 | src: "remark", 7 | }); 8 | 9 | site.use(remark()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/render_order.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | 3 | Deno.test("render order property", async (t) => { 4 | const site = getSite({ 5 | src: "render_order", 6 | }); 7 | 8 | await build(site); 9 | await assertSiteSnapshot(t, site); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/resolve_urls.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import resolveUrls from "../plugins/resolve_urls.ts"; 3 | import slugifyUrls from "../plugins/slugify_urls.ts"; 4 | 5 | Deno.test("relative_url plugin", async (t) => { 6 | const site = getSite({ 7 | src: "resolve_urls", 8 | }); 9 | 10 | site.add("statics", ""); 11 | site.use(resolveUrls()); 12 | site.use(slugifyUrls()); // Test combined with slugify_urls 13 | 14 | await build(site); 15 | await assertSiteSnapshot(t, site); 16 | }); 17 | 18 | Deno.test("relative_url plugin (without slugify)", async (t) => { 19 | const site = getSite({ 20 | src: "resolve_urls", 21 | }); 22 | 23 | site.add("statics", ""); 24 | site.use(resolveUrls()); 25 | 26 | await build(site); 27 | await assertSiteSnapshot(t, site); 28 | }); 29 | -------------------------------------------------------------------------------- /tests/sass.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import sass from "../plugins/sass.ts"; 3 | 4 | Deno.test("SASS plugin", async (t) => { 5 | const site = getSite({ 6 | src: "sass", 7 | }); 8 | site.add([".scss", ".sass"]); 9 | site.use(sass()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/sheets.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import sheets from "../plugins/sheets.ts"; 3 | 4 | Deno.test("Sheets plugin", async (t) => { 5 | const site = getSite({ 6 | src: "sheets", 7 | }); 8 | 9 | site.use(sheets()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/source_maps.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import sass from "../plugins/sass.ts"; 3 | import postcss from "../plugins/postcss.ts"; 4 | import lightningcss from "../plugins/lightningcss.ts"; 5 | import sourceMaps from "../plugins/source_maps.ts"; 6 | import esbuild from "../plugins/esbuild.ts"; 7 | import terser from "../plugins/terser.ts"; 8 | 9 | Deno.test("Source maps from CSS files", async (t) => { 10 | const site = getSite({ 11 | src: "sass", 12 | }); 13 | 14 | site.add([".scss", ".sass", ".css"]); 15 | site.use(sass()); 16 | site.use(postcss()); 17 | site.use(lightningcss({ 18 | includes: false, 19 | })); 20 | site.use(sourceMaps()); 21 | 22 | await build(site); 23 | await assertSiteSnapshot(t, site); 24 | }); 25 | 26 | Deno.test("Source maps from Js files", { 27 | sanitizeOps: false, 28 | sanitizeResources: false, 29 | }, async (t) => { 30 | const site = getSite({ 31 | src: "esbuild", 32 | }); 33 | site.add([".ts"]); 34 | site.use(esbuild()); 35 | site.use(terser()); 36 | site.use(sourceMaps()); 37 | 38 | await build(site); 39 | await assertSiteSnapshot(t, site); 40 | }); 41 | -------------------------------------------------------------------------------- /tests/sri.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import sri from "../plugins/sri.ts"; 3 | 4 | Deno.test("SRI plugin", async (t) => { 5 | const site = getSite({ 6 | src: "sri", 7 | }); 8 | 9 | site.use(sri()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/static_files.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | 3 | Deno.test("Copy static files", async (t) => { 4 | const site = getSite({ 5 | src: "static_files", 6 | }); 7 | 8 | site.add("static", "."); 9 | site.add("other"); 10 | site.add( 11 | "posts/2022-01-01_first-post/assets", 12 | (file) => file.replace(".scss", ".css"), 13 | ); 14 | site.add("posts/2022-01-01_first-post/individual-file"); 15 | site.add([".css", ".js"]); 16 | site.add([".copy"]); 17 | site.add("_headers"); 18 | site.add("static/_redirects", "_redirects"); 19 | site.add( 20 | [".copy2"], 21 | (file) => "/subdir" + file.replace(/\.copy2/, ".copy3"), 22 | ); 23 | 24 | // copied with the trailing slash 25 | site.add("other2/"); 26 | 27 | // not copied because of the trailing slash 28 | site.add("three.no/"); 29 | 30 | // Copy a directory inside a ignored directory 31 | site.add("_static/inner", "inner"); 32 | 33 | await build(site); 34 | await assertSiteSnapshot(t, site); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/svgo.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import svgo from "../plugins/svgo.ts"; 3 | 4 | Deno.test("SVGO plugin", async (t) => { 5 | const site = getSite({ 6 | src: "svgo", 7 | }); 8 | 9 | site.add("favicon.svg"); 10 | site.use(svgo()); 11 | 12 | await build(site); 13 | await assertSiteSnapshot(t, site); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/symlinks.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import nunjucks from "../plugins/nunjucks.ts"; 3 | 4 | Deno.test("follow symlinks", async (t) => { 5 | const site = getSite({ 6 | src: "symlinks/src", 7 | }); 8 | 9 | site.use(nunjucks()); 10 | 11 | await build(site); 12 | await assertSiteSnapshot(t, site); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/tailwindcss.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import tailwindcss from "../plugins/tailwindcss.ts"; 3 | import sourceMaps from "../plugins/source_maps.ts"; 4 | 5 | Deno.test("Tailwindcss plugin", async (t) => { 6 | const site = getSite({ 7 | src: "tailwindcss", 8 | }); 9 | 10 | site.add([".css"]); 11 | site.use(tailwindcss()); 12 | 13 | await build(site); 14 | await assertSiteSnapshot(t, site); 15 | }); 16 | 17 | Deno.test("Tailwindcss plugin + minify", async (t) => { 18 | const site = getSite({ 19 | src: "tailwindcss", 20 | }); 21 | 22 | site.add([".css"]); 23 | site.use(tailwindcss({ 24 | minify: true, 25 | })); 26 | 27 | await build(site); 28 | await assertSiteSnapshot(t, site); 29 | }); 30 | 31 | Deno.test("Tailwindcss plugin + source maps", async (t) => { 32 | const site = getSite({ 33 | src: "tailwindcss", 34 | }); 35 | 36 | site.add([".css"]); 37 | site.use(tailwindcss()); 38 | site.use(sourceMaps()); 39 | 40 | await build(site); 41 | await assertSiteSnapshot(t, site); 42 | }); 43 | -------------------------------------------------------------------------------- /tests/terser.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import terser from "../plugins/terser.ts"; 3 | 4 | Deno.test("terser plugin", async (t) => { 5 | const site = getSite({ 6 | src: "terser", 7 | }); 8 | 9 | site.add([".js"]); 10 | site.use(terser()); 11 | 12 | await build(site); 13 | await assertSiteSnapshot(t, site); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/toml.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | 3 | Deno.test("TOML plugin", async (t) => { 4 | const site = getSite({ 5 | src: "toml", 6 | }); 7 | 8 | await build(site); 9 | await assertSiteSnapshot(t, site); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/transform_images.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | import imageTransform from "../plugins/transform_images.ts"; 3 | 4 | Deno.test("Image transform plugin", async (t) => { 5 | const site = getSite({ 6 | src: "transform_images", 7 | }); 8 | 9 | site.add("/"); 10 | site.use(imageTransform()); 11 | 12 | await build(site); 13 | await assertSiteSnapshot(t, site); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/url.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | 3 | Deno.test("url and htmlUrl update href", async (t) => { 4 | const site = getSite({ 5 | src: "url", 6 | location: new URL("https://example.com/test/"), 7 | }); 8 | 9 | await build(site); 10 | await assertSiteSnapshot(t, site); 11 | }); 12 | -------------------------------------------------------------------------------- /tests/vento.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | 3 | Deno.test("build a site with vento", async (t) => { 4 | const site = getSite({ 5 | src: "vento", 6 | location: new URL("https://example.com/blog"), 7 | }); 8 | 9 | site.filter("upper", (value: string) => value.toUpperCase()); 10 | site.filter("fromPage", function (key) { 11 | return this?.data?.[key]; 12 | }); 13 | site.filter("fromPageAsync", function (key) { 14 | return Promise.resolve(this?.data?.[key]); 15 | }, true); 16 | 17 | await build(site); 18 | await assertSiteSnapshot(t, site); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/well_known.test.ts: -------------------------------------------------------------------------------- 1 | import { assertSiteSnapshot, build, getSite } from "./utils.ts"; 2 | 3 | Deno.test(".well-known folder", async (t) => { 4 | const site = getSite({ 5 | src: "well_known", 6 | }); 7 | 8 | await build(site); 9 | await assertSiteSnapshot(t, site); 10 | }); 11 | -------------------------------------------------------------------------------- /tests/www.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResponseSnapshot, getServer } from "./utils.ts"; 2 | import www from "../middlewares/www.ts"; 3 | 4 | Deno.test("www middleware", async (t) => { 5 | const server = getServer(); 6 | 7 | server.use(www()); 8 | 9 | await assertResponseSnapshot(t, server, new Request("http://www.localhost")); 10 | await assertResponseSnapshot(t, server, new Request("http://localhost")); 11 | await assertResponseSnapshot(t, server, new Request("https://www.localhost")); 12 | }); 13 | 14 | Deno.test("www middleware (add www)", async (t) => { 15 | const server = getServer(); 16 | 17 | server.use(www({ 18 | add: true, 19 | })); 20 | 21 | await assertResponseSnapshot(t, server, new Request("http://www.localhost")); 22 | await assertResponseSnapshot(t, server, new Request("http://localhost")); 23 | await assertResponseSnapshot(t, server, new Request("https://www.localhost")); 24 | }); 25 | -------------------------------------------------------------------------------- /types.ts: -------------------------------------------------------------------------------- 1 | import type { Engine, Helper } from "./core/renderer.ts"; 2 | import type { Data as PageData, Page } from "./core/file.ts"; 3 | import type { Loader } from "./core/fs.ts"; 4 | import type { default as Site, Plugin } from "./core/site.ts"; 5 | import type { Archetype } from "./cli/create.ts"; 6 | import type { Middleware, RequestHandler } from "./core/server.ts"; 7 | 8 | declare global { 9 | namespace Lume { 10 | export type { 11 | Archetype, 12 | Engine, 13 | Loader, 14 | Middleware, 15 | Page, 16 | Plugin, 17 | RequestHandler, 18 | Site, 19 | }; 20 | 21 | /** The page data */ 22 | export interface Data extends PageData { 23 | // deno-lint-ignore no-explicit-any 24 | [index: string]: any; 25 | } 26 | 27 | /** The page helpers */ 28 | export interface Helpers { 29 | [key: string]: Helper; 30 | } 31 | } 32 | } 33 | --------------------------------------------------------------------------------