diff options
author | 2021-03-21 00:44:42 -0700 | |
---|---|---|
committer | 2021-03-21 00:44:42 -0700 | |
commit | 417657f138fbc5e194df3dd511e3b9c8e53920fd (patch) | |
tree | c15f73c625d3c222304557f4f753204c65304607 | |
parent | 2082001ff8702ec48072b59caafe85573a3b2891 (diff) | |
download | astro-417657f138fbc5e194df3dd511e3b9c8e53920fd.tar.gz astro-417657f138fbc5e194df3dd511e3b9c8e53920fd.tar.zst astro-417657f138fbc5e194df3dd511e3b9c8e53920fd.zip |
lots of improvements
31 files changed, 513 insertions, 475 deletions
diff --git a/examples/snowpack/astro/components/Banner.hmx b/examples/snowpack/astro/components/Banner.hmx index 233eedb3f..8879bf939 100644 --- a/examples/snowpack/astro/components/Banner.hmx +++ b/examples/snowpack/astro/components/Banner.hmx @@ -1,12 +1,10 @@ -<Component> - <section class="grid-banner"> - <div class="notification"> - <div class="container"> - Snowpack 3.0 is out now! - <a href="/posts/2021-01-13-snowpack-3-0"> - Read the announcement post → - </a> - </div> +<section class="grid-banner"> + <div class="notification"> + <div class="container"> + Snowpack 3.0 is out now! + <a href="/posts/2021-01-13-snowpack-3-0"> + Read the announcement post → + </a> </div> - </section> -</Component>
\ No newline at end of file + </div> +</section>
\ No newline at end of file diff --git a/examples/snowpack/astro/components/Hero.hmx b/examples/snowpack/astro/components/Hero.hmx index e3dabe87a..d77f48573 100644 --- a/examples/snowpack/astro/components/Hero.hmx +++ b/examples/snowpack/astro/components/Hero.hmx @@ -1,5 +1,4 @@ -<Component> - <div class="hero"> +<div class="hero"> <div class="section"> <h1 class="header-snowpack">Snowpack</h1> <p class="header-snowpack-subtitle">The faster frontend build tool.</p> @@ -7,11 +6,15 @@ <div class="hero-cta"> <a href="/tutorials/quick-start" class="button button-primary">Get started</a> <div style="margin: 0.5rem"></div> - <button id="copy-button" class="copy-button hidden-mobile" data-clipboard-text="npm install snowpack"> + <button id="copy-button" class="copy-button hidden-mobile" data-clipboard-text="npm install snowpack"> <span class="faded" style="margin-right: 0.75rem;">$</span> <span id="copy-button-text">npm install snowpack</span> - <svg style="height: 22px;width: 22px;margin: -0.1rem -0.1rem 0 0.75rem;" aria-hidden="true" focusable="false" data-prefix="far" data-icon="clone" class="svg-inline--fa fa-clone fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512"> - <path fill="currentColor" d="M464 0H144c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h320c26.51 0 48-21.49 48-48v-48h48c26.51 0 48-21.49 48-48V48c0-26.51-21.49-48-48-48zM362 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h42v224c0 26.51 21.49 48 48 48h224v42a6 6 0 0 1-6 6zm96-96H150a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h308a6 6 0 0 1 6 6v308a6 6 0 0 1-6 6z"></path> + <svg style="height: 22px;width: 22px;margin: -0.1rem -0.1rem 0 0.75rem;" aria-hidden="true" focusable="false" + data-prefix="far" data-icon="clone" class="svg-inline--fa fa-clone fa-w-16" role="img" + xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512"> + <path fill="currentColor" + d="M464 0H144c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h320c26.51 0 48-21.49 48-48v-48h48c26.51 0 48-21.49 48-48V48c0-26.51-21.49-48-48-48zM362 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h42v224c0 26.51 21.49 48 48 48h224v42a6 6 0 0 1-6 6zm96-96H150a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h308a6 6 0 0 1 6 6v308a6 6 0 0 1-6 6z"> + </path> </svg> </button> <script type="module"> @@ -19,10 +22,10 @@ const clippy = new Clipboard('.copy-button'); const copyText = document.getElementById('copy-button').innerHTML; clippy.on('success', function (e) { - document.getElementById('copy-button').style.minWidth = document.getElementById('copy-button').offsetWidth; + document.getElementById('copy-button').style.minWidth = document.getElementById('copy-button').offsetWidth; console.info('Trigger:', e); document.getElementById('copy-button').innerHTML = '<span>copied to clipboard!</span>'; - document.getElementById('copy-button').addEventListener("mouseleave", function( event ) { + document.getElementById('copy-button').addEventListener("mouseleave", function (event) { document.getElementById('copy-button').innerHTML = copyText; }, false); @@ -39,9 +42,9 @@ <div class="hero-cta"> <!-- Place this tag where you want the button to render. --> <div class="hidden-mobile" style="text-align: center; height: 36px;"> - <a class="github-button" href="https://github.com/snowpackjs/snowpack" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star snowpackjs/snowpack on GitHub">Star</a > + <a class="github-button" href="https://github.com/snowpackjs/snowpack" data-icon="octicon-star" + data-size="large" data-show-count="true" aria-label="Star snowpackjs/snowpack on GitHub">Star</a> </div> </div> </div> -</div> -</Component> +</div>
\ No newline at end of file diff --git a/examples/snowpack/astro/components/Menu.hmx b/examples/snowpack/astro/components/Menu.hmx index b1bc38a77..f94e28ee2 100644 --- a/examples/snowpack/astro/components/Menu.hmx +++ b/examples/snowpack/astro/components/Menu.hmx @@ -1,4 +1,3 @@ -<Component> <nav class="snow-toc"> <ol class="snow-toc-contents"> <li class="snow-toc-section"> @@ -83,4 +82,3 @@ </li> </ol> </nav> -</Component> diff --git a/examples/snowpack/astro/components/Nav.hmx b/examples/snowpack/astro/components/Nav.hmx index 3d3ed1ac1..4a463634c 100644 --- a/examples/snowpack/astro/components/Nav.hmx +++ b/examples/snowpack/astro/components/Nav.hmx @@ -1,21 +1,27 @@ -<Component> <nav class="snow-nav"> - <button id="toc-drawer-button" class="snow-nav-mobile-open" type="button" aria-expanded="false" aria-controls="nav-primary"> + <button id="toc-drawer-button" class="snow-nav-mobile-open" type="button" aria-expanded="false" + aria-controls="nav-primary"> <svg focusable="false" class="snow-icon" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 448 512"> <title>Toggle mobile menu</title> - <path d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"></path> + <path + d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"> + </path> </svg> </button> <a class="snow-nav-logo snow-logo" href="/"> - <svg class="snow-logo-icon" viewbox="0 0 640 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <svg class="snow-logo-icon" viewbox="0 0 640 512" version="1.1" xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> <g transform="translate(-1.000000, 0.000000)" fill-rule="nonzero"> - <path d="M635.92,462.7 L347.92,14.7 C342.03,5.54 331.89,0 321,0 C310.11,0 299.97,5.54 294.08,14.7 L6.08,462.7 C-0.250773249,472.547007 -0.699487627,485.064987 4.91,495.34 C10.522069,505.612419 21.2945349,512 33,512 L609,512 C620.71,512 631.48,505.61 637.09,495.33 C642.699457,485.058495 642.250708,472.543372 635.92,462.7 Z M321,91.18 L406.39,224 L321,224 L257,288 L218.94,249.94 L321,91.18 Z" id="Shape"></path> + <path + d="M635.92,462.7 L347.92,14.7 C342.03,5.54 331.89,0 321,0 C310.11,0 299.97,5.54 294.08,14.7 L6.08,462.7 C-0.250773249,472.547007 -0.699487627,485.064987 4.91,495.34 C10.522069,505.612419 21.2945349,512 33,512 L609,512 C620.71,512 631.48,505.61 637.09,495.33 C642.699457,485.058495 642.250708,472.543372 635.92,462.7 Z M321,91.18 L406.39,224 L321,224 L257,288 L218.94,249.94 L321,91.18 Z" + id="Shape"></path> </g> </svg> <span class="snow-logo-type">Snowpack</span> </a> <div class="search-form"> - <input type="text" name="search" placeholder="Search documentation..." class="search-form-input" id="search-form-input"> + <input type="text" name="search" placeholder="Search documentation..." class="search-form-input" + id="search-form-input"> <span class="search-form-hint"> <span class="sr-only">Press </span> <kbd class="font-sans"><abbr title="Command" style="text-decoration: none;">⌘</abbr></kbd> @@ -29,64 +35,75 @@ {`v${props.version}`} </a> <a href="https://github.com/snowpackjs/snowpack" target="_blank" class="snow-nav-link snow-nav-link__desktop"> - <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="snow-icon" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 496 512"> - <path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path> + <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="snow-icon" role="img" + xmlns="http://www.w3.org/2000/svg" viewbox="0 0 496 512"> + <path fill="currentColor" + d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"> + </path> </svg> </a> <a href="https://twitter.com/snowpackjs" target="_blank" class="snow-nav-link snow-nav-link__desktop"> - <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" class="snow-icon" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512"> - <path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path> + <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" class="snow-icon" role="img" + xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512"> + <path fill="currentColor" + d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"> + </path> </svg> </a> <a href="https://discord.gg/snowpack" target="_blank" class="snow-nav-link snow-nav-link__desktop"> - <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="discord" class="snow-icon" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 210 240"><path d="M84.79 90.45c-6.45 0-11.55 5.66-11.55 12.57s5.21 12.57 11.55 12.57c6.45 0 11.55-5.66 11.55-12.57.11-6.91-5.09-12.57-11.55-12.57zm41.32 0c-6.45 0-11.55 5.66-11.55 12.57s5.21 12.57 11.55 12.57c6.45 0 11.55-5.66 11.55-12.57s-5.09-12.57-11.55-12.57z"/><path fill="currentColor" d="M185.4 0H24.6C11.04 0 0 11.04 0 24.72v162.24c0 13.68 11.04 24.72 24.6 24.72h136.08l-6.36-22.2 15.36 14.28 14.52 13.44L210 240V24.72C210 11.04 198.96 0 185.4 0zm-46.32 156.72s-4.32-5.16-7.92-9.72c15.72-4.44 21.72-14.28 21.72-14.28-4.92 3.24-9.6 5.52-13.8 7.08-6 2.52-11.76 4.2-17.4 5.16-11.52 2.16-22.08 1.56-31.08-.12-6.84-1.32-12.72-3.24-17.64-5.16-2.76-1.08-5.76-2.4-8.76-4.08-.36-.24-.72-.36-1.08-.6-.24-.12-.36-.24-.48-.36-2.16-1.2-3.36-2.04-3.36-2.04s5.76 9.6 21 14.16c-3.6 4.56-8.04 9.96-8.04 9.96-26.52-.84-36.6-18.24-36.6-18.24 0-38.64 17.28-69.96 17.28-69.96 17.28-12.96 33.72-12.6 33.72-12.6l1.2 1.44c-21.6 6.24-31.56 15.72-31.56 15.72s2.64-1.44 7.08-3.48c12.84-5.64 23.04-7.2 27.24-7.56.72-.12 1.32-.24 2.04-.24 7.32-.96 15.6-1.2 24.24-.24 11.4 1.32 23.64 4.68 36.12 11.52 0 0-9.48-9-29.88-15.24l1.68-1.92s16.44-.36 33.72 12.6c0 0 17.28 31.32 17.28 69.96 0 0-10.2 17.4-36.72 18.24z"/></svg> + <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="discord" class="snow-icon" role="img" + xmlns="http://www.w3.org/2000/svg" viewbox="0 0 210 240"> + <path + d="M84.79 90.45c-6.45 0-11.55 5.66-11.55 12.57s5.21 12.57 11.55 12.57c6.45 0 11.55-5.66 11.55-12.57.11-6.91-5.09-12.57-11.55-12.57zm41.32 0c-6.45 0-11.55 5.66-11.55 12.57s5.21 12.57 11.55 12.57c6.45 0 11.55-5.66 11.55-12.57s-5.09-12.57-11.55-12.57z" /> + <path fill="currentColor" + d="M185.4 0H24.6C11.04 0 0 11.04 0 24.72v162.24c0 13.68 11.04 24.72 24.6 24.72h136.08l-6.36-22.2 15.36 14.28 14.52 13.44L210 240V24.72C210 11.04 198.96 0 185.4 0zm-46.32 156.72s-4.32-5.16-7.92-9.72c15.72-4.44 21.72-14.28 21.72-14.28-4.92 3.24-9.6 5.52-13.8 7.08-6 2.52-11.76 4.2-17.4 5.16-11.52 2.16-22.08 1.56-31.08-.12-6.84-1.32-12.72-3.24-17.64-5.16-2.76-1.08-5.76-2.4-8.76-4.08-.36-.24-.72-.36-1.08-.6-.24-.12-.36-.24-.48-.36-2.16-1.2-3.36-2.04-3.36-2.04s5.76 9.6 21 14.16c-3.6 4.56-8.04 9.96-8.04 9.96-26.52-.84-36.6-18.24-36.6-18.24 0-38.64 17.28-69.96 17.28-69.96 17.28-12.96 33.72-12.6 33.72-12.6l1.2 1.44c-21.6 6.24-31.56 15.72-31.56 15.72s2.64-1.44 7.08-3.48c12.84-5.64 23.04-7.2 27.24-7.56.72-.12 1.32-.24 2.04-.24 7.32-.96 15.6-1.2 24.24-.24 11.4 1.32 23.64 4.68 36.12 11.52 0 0-9.48-9-29.88-15.24l1.68-1.92s16.44-.36 33.72 12.6c0 0 17.28 31.32 17.28 69.96 0 0-10.2 17.4-36.72 18.24z" /> + </svg> </a> </nav> - <script> - function handleMobileNav(evt) { - evt.preventDefault(); - /*If hidden-mobile class is enabled that means we are on desktop do overflow normal but we - if we are at mobile fixed body position, so that its not scrollable(which currently causing bug) and navbar handling its - owns scroll. Case to consider there are chance use can open navbar using toggle button and user when click on any link - body postion should be unset - */ - document.body.classList.toggle('is-nav-open'); - const isOpen = document.body.classList.contains('is-nav-open'); - if (isOpen) { - evt.target.setAttribute('aria-expanded', 'true'); - } else { - evt.target.setAttribute('aria-expanded', 'false'); - } +<script> + function handleMobileNav(evt) { + evt.preventDefault(); + /*If hidden-mobile class is enabled that means we are on desktop do overflow normal but we + if we are at mobile fixed body position, so that its not scrollable(which currently causing bug) and navbar handling its + owns scroll. Case to consider there are chance use can open navbar using toggle button and user when click on any link + body postion should be unset + */ + document.body.classList.toggle('is-nav-open'); + const isOpen = document.body.classList.contains('is-nav-open'); + if (isOpen) { + evt.target.setAttribute('aria-expanded', 'true'); + } else { + evt.target.setAttribute('aria-expanded', 'false'); } + } - const mobileNavBtn = document.getElementById('toc-drawer-button'); - mobileNavBtn.addEventListener('click', handleMobileNav); - mobileNavBtn.addEventListener('touchend', handleMobileNav); - if (window.location.pathname.startsWith('/posts')) { - mobileNavBtn.style.display = 'none'; - } + const mobileNavBtn = document.getElementById('toc-drawer-button'); + mobileNavBtn.addEventListener('click', handleMobileNav); + mobileNavBtn.addEventListener('touchend', handleMobileNav); + if (window.location.pathname.startsWith('/posts')) { + mobileNavBtn.style.display = 'none'; + } - const searchFormInputEl = document.getElementById('search-form-input'); - searchFormInputEl.addEventListener('keyup', () => { + const searchFormInputEl = document.getElementById('search-form-input'); + searchFormInputEl.addEventListener('keyup', () => { const gridTocEl = document.querySelector('#nav-primary'); - if (searchFormInputEl.value) { - gridTocEl.classList.add('is-mobile-hidden'); - } else { - gridTocEl.classList.remove('is-mobile-hidden'); - } - }); + if (searchFormInputEl.value) { + gridTocEl.classList.add('is-mobile-hidden'); + } else { + gridTocEl.classList.remove('is-mobile-hidden'); + } + }); - document.onkeydown = function (e) { - if ((e.ctrlKey || e.metaKey) && e.which == 75) { - e.preventDefault(); - searchFormInputEl.focus(); - } - }; + document.onkeydown = function (e) { + if ((e.ctrlKey || e.metaKey) && e.which == 75) { + e.preventDefault(); + searchFormInputEl.focus(); + } + }; </script> <script type="module" defer> import docsearch from 'docsearch.js/dist/cdn/docsearch.min.js'; docsearch({ apiKey: '562139304880b94536fc53f5d65c5c19', indexName: 'snowpack', inputSelector: '.search-form-input', debug: true // Set debug to true if you want to inspect the dropdown }); -</script> -</Component> +</script>
\ No newline at end of file diff --git a/examples/snowpack/astro/components/Subnav.hmx b/examples/snowpack/astro/components/Subnav.hmx index 49900e04b..3e2834595 100644 --- a/examples/snowpack/astro/components/Subnav.hmx +++ b/examples/snowpack/astro/components/Subnav.hmx @@ -1,19 +1,21 @@ -<Component> - <script type="module" defer src="/js/index.js"></script> - <aside class="snow-toc snow-toc__subnav snow-view-subnav"> - <h2 class="content-title"> - {props.title} - </h2> +<script type="module" defer src="/js/index.js"></script> +<aside class="snow-toc snow-toc__subnav snow-view-subnav"> + <h2 class="content-title"> + {props.title} + </h2> + + {props.headers.length > 0 && <div> <h4 class="snow-toc-section-header">On this page</h4> <nav class="toc"> <ol> - {props.headings.map((heading) => { - return <li><a href={heading.url}>{heading.text}</a></li> + {props.headers.map((heading) => { + return <li><a href={"#" + heading.slug}>{heading.text}</a></li> })} </ol> </nav> <hr /> - <h4 class="snow-toc-section-header">Suggest a change</h4> - <a href="https://github.com/snowpackjs/snowpack/blob/main/www/{props.inputPath}">Edit this page on GitHub</a> - </aside> -</Component>
\ No newline at end of file + </div>} + + <h4 class="snow-toc-section-header">Suggest a change</h4> + <a href="https://github.com/snowpackjs/snowpack/blob/main/www/{props.inputPath}">Edit this page on GitHub</a> +</aside>
\ No newline at end of file diff --git a/examples/snowpack/astro/layouts/base.hmx b/examples/snowpack/astro/layouts/base.hmx index 75150ad5d..c002416e7 100644 --- a/examples/snowpack/astro/layouts/base.hmx +++ b/examples/snowpack/astro/layouts/base.hmx @@ -1,56 +1,56 @@ -<script hmx="setup"> +<script astro> import Banner from '../components/Banner.hmx'; import Nav from '../components/Nav.hmx'; - export function setup() { + export function setup({ context }) { return { - title: 'Snowpack', - description: 'Snowpack is a lightning-fast frontend build tool, designed for the modern web.', - props: { - version: '3.0.13', + context: { + title: 'Snowpack', + description: 'Snowpack is a lightning-fast frontend build tool, designed for the modern web.', + currentSnowpackVersion: '3.0.13', } }; } </script> -<head> +<slot:head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /> <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" /> <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png" /> - <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"/> + <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png" /> <link rel="manifest" href="/favicon/site.webmanifest" /> <!-- Primary Meta Tags --> - <title>{title}</title> - <meta name="title" content={title} /> - <meta name="description" content="{description}" /> + <title>{context.title}</title> + <meta name="title" content={context.title} /> + <meta name="description" content="{context.description}" /> <!-- Open Graph / Facebook --> - <meta property="og:type" content="website"/> - <meta property="og:url" content="https://www.snowpack.dev{props.permalink}"/> - <meta property="og:title" content={title}/> - <meta property="og:description" content={description}/> - <meta property="og:image" content="https://www.snowpack.dev/img/social-2.jpg"/> + <meta property="og:type" content="website" /> + <meta property="og:url" content={context.permalink}/> + <meta property="og:title" content={context.title} /> + <meta property="og:description" content={context.description} /> + <meta property="og:image" content="https://www.snowpack.dev/img/social-2.jpg" /> <!-- Twitter --> - <meta property="twitter:card" content="summary_large_image"/> - <meta property="twitter:url" content="https://www.snowpack.dev{props.permalink}"/> - <meta property="twitter:title" content={title}/> - <meta property="twitter:description" content={description}/> - <meta property="twitter:image" content="https://www.snowpack.dev/img/social-2.jpg"/> + <meta property="twitter:card" content="summary_large_image" /> + <meta property="twitter:url" content={context.permalink}/> + <meta property="twitter:title" content={context.title} /> + <meta property="twitter:description" content={context.description} /> + <meta property="twitter:image" content="https://www.snowpack.dev/img/social-2.jpg" /> <!-- Global Stylesheets --> <link rel="stylesheet" href="/css/app.css" /> - <link href="https://fonts.googleapis.com/css2?family=Overpass:wght@400;700;900&display=swap" rel="stylesheet"/> + <link href="https://fonts.googleapis.com/css2?family=Overpass:wght@400;700;900&display=swap" rel="stylesheet" /> <!-- Note: You can then add additional, custom things here as well. --> <!-- if no slot given, assume add to bottom --> <slot></slot> -</head> +</slot:head> -<body class="base 2"> +<slot:body> <Banner></Banner> - <Nav version={props.version} /> + <Nav version={context.currentSnowpackVersion} /> <!-- if no slot given, assume add to bottom --> <slot></slot> @@ -65,4 +65,4 @@ gtag('js', new Date()); gtag('config', 'UA-130280175-9', { page_path: location.pathname === '/' ? (location.pathname + location.hash) : (location.pathname) }); </script> -</body> +</slot:body>
\ No newline at end of file diff --git a/examples/snowpack/astro/layouts/content-with-cover.hmx b/examples/snowpack/astro/layouts/content-with-cover.hmx index 7ec875a3c..2934ec307 100644 --- a/examples/snowpack/astro/layouts/content-with-cover.hmx +++ b/examples/snowpack/astro/layouts/content-with-cover.hmx @@ -1,12 +1,14 @@ -<script hmx="setup"> +<script astro> import Menu from '../components/Menu.hmx'; import Subnav from '../components/Subnav.hmx'; - export function setup() { - return {layout: 'layouts/base.hmx', props: {}}; + + export const layout = 'layouts/base.hmx'; + export function setup({ context }) { + return {}; } </script> -<head> +<slot:head> <style> .cover-wrapper { width: 100%; @@ -47,13 +49,13 @@ </style> <slot></slot> -</head> +</slot:head> -<body> +<slot:body> <div class="cover-wrapper"> - <img class="cover-blur" src={props.cover} alt=""/> - <img class="cover" src={props.cover} alt=""/> + <img class="cover-blur" src={context.cover} alt=""/> + <img class="cover" src={context.cover} alt=""/> </div> <div class="container"> @@ -66,7 +68,7 @@ <article class="snow-view-main"> <div class="content"> <h2 class="content-title"> - {title} + {context.title} </h2> <div class="content-layout"> <div class="content-body"> @@ -76,7 +78,7 @@ </div> </article> - <Subnav title={title} /> + <Subnav title={context.title} headers={context.content.headers} /> </section> </div> -</body>
\ No newline at end of file +</slot:body>
\ No newline at end of file diff --git a/examples/snowpack/astro/layouts/content.hmx b/examples/snowpack/astro/layouts/content.hmx index 2ca634a44..e040a47bf 100644 --- a/examples/snowpack/astro/layouts/content.hmx +++ b/examples/snowpack/astro/layouts/content.hmx @@ -1,33 +1,22 @@ -<script hmx="setup"> +<script astro> import Subnav from '../components/Subnav.hmx'; import Menu from '../components/Menu.hmx'; - import markdownToAst from "mdast-util-from-markdown"; - import markdownTableOfContents from "mdast-util-toc"; - export function setup({content}) { - const mdAst = markdownToAst(content.source); - // This is super gross, and just me trying to get an existing table of contents generator to work - // we can either do this internally as helpful sugar, or build a simpler one as a plugin - const headingsAst = markdownTableOfContents(mdAst); - const headings = headingsAst.map ? headingsAst.map.children.map(ch => { - return {depth: 1, url: ch.children[0].children[0].url, text: ch.children[0].children[0].children[0].value}; - }) : []; - - return { - layout: 'layouts/base.hmx', - props: { - headings, - } + export const layout = 'layouts/base.hmx'; + export function setup({ context }) { + return { + context: { + } }; } </script> -<head> +<slot:head> <slot></slot> -</head> +</slot:head> -<body> +<slot:body> <div class="container"> <section class="snow-view__docs has-subnav"> @@ -35,12 +24,12 @@ <Menu /> </aside> - <Subnav title={title} headings={props.headings} /> + <Subnav title={context.title} headers={context.content.headers} /> <article class="snow-view-main"> <div class="content"> <h2 class="content-title"> - {title} + {context.title} </h2> <div class="content-layout"> <div class="content-body"> @@ -52,4 +41,4 @@ </section> </div> -</body>
\ No newline at end of file +</slot:body>
\ No newline at end of file diff --git a/examples/snowpack/astro/layouts/main.hmx b/examples/snowpack/astro/layouts/main.hmx index e2bbac677..c0793e3b8 100644 --- a/examples/snowpack/astro/layouts/main.hmx +++ b/examples/snowpack/astro/layouts/main.hmx @@ -1,20 +1,18 @@ -<script hmx="setup"> +<script astro> import Menu from '../components/Menu.hmx'; - export function setup() { - return { - layout: 'layouts/base.hmx', - props: {} - }; + export const layout = 'layouts/base.hmx'; + export function setup({ context }) { + return {}; } </script> -<head> +<slot:head> <!-- hi --> <slot></slot> -</head> +</slot:head> -<body class="base 2"> +<slot:body> <div class="container"> <section class="snow-view__docs is-full"> @@ -28,4 +26,4 @@ </section> </div> -</body>
\ No newline at end of file +</slot:body>
\ No newline at end of file diff --git a/examples/snowpack/astro/layouts/post.hmx b/examples/snowpack/astro/layouts/post.hmx index 6e5841633..fef70a4ac 100644 --- a/examples/snowpack/astro/layouts/post.hmx +++ b/examples/snowpack/astro/layouts/post.hmx @@ -1,11 +1,12 @@ -<script hmx="setup"> +<script astro> import {format as formatDate, parseISO} from 'date-fns'; - export function setup() { - return { layout: 'layouts/base.hmx', props: {} }; + export const layout = 'layouts/base.hmx'; + export function setup({ context }) { + return {}; } </script> -<head> +<slot:head> <link rel="stylesheet" href="/css/legacy-post.css" /> <style> .markdown-body img, @@ -99,10 +100,9 @@ } </style> <slot></slot> -</head> - -<body> +</slot:head> +<slot:body> <div class="grid-extra-space"> <div class="grid-body-header"> <svg height="80px" style="padding-left: 8px;" viewBox="0 0 640 512" version="1.1" @@ -115,7 +115,7 @@ </g> </g> </svg> - <h1 class="header-snowpack">{title}</h1> + <h1 class="header-snowpack">{context.title}</h1> <p> @@ -140,4 +140,4 @@ <slot></slot> </article> </div> -</body>
\ No newline at end of file +</slot:body>
\ No newline at end of file diff --git a/examples/snowpack/astro/pages/404.hmx b/examples/snowpack/astro/pages/404.hmx index a1799cf6e..f82e303ff 100644 --- a/examples/snowpack/astro/pages/404.hmx +++ b/examples/snowpack/astro/pages/404.hmx @@ -1,16 +1,17 @@ -<script hmx="setup"> - export function setup() { +<script astro> + export const layout = 'layouts/main.hmx'; + + export function setup({ context }) { return { - title: '404 - Not Found', - layout: 'layouts/main.hmx', - props: { + context: { + title: '404 - Not Found', } }; } </script> <h2 class="content-title"> - {title} + {context.title} </h2> <div class="content"> diff --git a/examples/snowpack/astro/pages/guides.hmx b/examples/snowpack/astro/pages/guides.hmx index 6660d2f79..20a22baaf 100644 --- a/examples/snowpack/astro/pages/guides.hmx +++ b/examples/snowpack/astro/pages/guides.hmx @@ -1,52 +1,56 @@ -<script hmx="setup"> +<script astro> import Card from '../components/Card.jsx'; + export const layout = 'layouts/main.hmx'; + // mocked for now, to be added later // 1. import {paginate} from 'magicthing'; // 2. export default function ({paginate}) { function paginate(options) { if (options.tag === 'guide') { return [ - {title: 'Test guide 1', href:"#"}, - {title: 'Test guide 2', href:"#"}, + { title: 'Test guide 1', href: "#" }, + { title: 'Test guide 2', href: "#" }, ]; } if (options.tag === 'communityGuides') { - return [{title: 'Test communityGuides', href:"#"}]; + return [{ title: 'Test communityGuides', href: "#" }]; } return []; } - export function setup({/* paginate */}) { + + export function setup({ context, /* paginate */ }) { return { - title: 'Guides', - description: "Snowpack's usage and integration guides.", - layout: 'layouts/main.hmx', - props: { - guides: paginate({ - files: '/posts/guides/*.md', - // sort: ((a, b) => new Date(b) - new Date(a)), - tag: 'guide', - limit: 10, - // page: query.page, - }), - communityGuides: paginate({ - files: '/posts/guides/*.md', - // sort: ((a, b) => new Date(b) - new Date(a)), - tag: 'communityGuides', - limit: 10, - }), + context: { + title: 'Guides', + description: "Snowpack's usage and integration guides.", + props: { + guides: paginate({ + files: '/posts/guides/*.md', + // sort: ((a, b) => new Date(b) - new Date(a)), + tag: 'guide', + limit: 10, + // page: query.page, + }), + communityGuides: paginate({ + files: '/posts/guides/*.md', + // sort: ((a, b) => new Date(b) - new Date(a)), + tag: 'communityGuides', + limit: 10, + }), + } } }; } </script> -<head> +<slot:head> -</head> +</slot:head> -<body> +<slot:body> <h2 class="content-title"> - {title} + {context.title} </h2> <h3 class="content-title"> @@ -56,13 +60,13 @@ <div class="content"> <ul> {props.guides.map((post) => { - return <li><a href={post.href}>{post.title}</a></li>; + return <li><a href={post.href}>{post.title}</a></li>; })} </ul> </div> - <br/> - <br/> + <br /> + <br /> <h3 class="content-title"> Popular Integration Guides @@ -70,7 +74,8 @@ <div class="card-grid card-grid-4"> {props.communityGuides.map((post) => { - return <Card item={post} />; + return + <Card item={post} />; })} </div> -</body>
\ No newline at end of file +</slot:body>
\ No newline at end of file diff --git a/examples/snowpack/astro/pages/index.hmx b/examples/snowpack/astro/pages/index.hmx index d32992920..03f03f8ce 100644 --- a/examples/snowpack/astro/pages/index.hmx +++ b/examples/snowpack/astro/pages/index.hmx @@ -1,22 +1,21 @@ -<script hmx="setup"> +<script astro> import Menu from '../components/Menu.hmx'; import Hero from '../components/Hero.hmx'; - export function setup() { - return { - layout: 'layouts/base.hmx', - props: {} - } + + export const layout = 'layouts/base.hmx'; + export function setup({context}) { + return {} } </script> -<head> - <!-- Head Stuff --> -</head> +<slot:head> + <meta charset="AAA" /> +</slot:head> -<body> - <Hero bar={title}></Hero> +<slot:body> + <Hero bar={context.title}></Hero> - <div foo={title} class="container" style="margin: 0 auto"> + <div foo={context.title} class="container" style="margin: 0 auto"> <section class="snow-view__docs is-full is-home"> <aside id="nav-primary" class="snow-view-nav"> @@ -95,4 +94,4 @@ <!-- Place this tag in your head or just before your close body tag. --> <script async="async" defer="defer" src="https://buttons.github.io/buttons.js"></script> -</body>
\ No newline at end of file +</slot:body>
\ No newline at end of file diff --git a/examples/snowpack/astro/pages/news.hmx b/examples/snowpack/astro/pages/news.hmx index 91595d625..5920cba4f 100644 --- a/examples/snowpack/astro/pages/news.hmx +++ b/examples/snowpack/astro/pages/news.hmx @@ -1,31 +1,33 @@ -<script hmx="setup"> +<script astro> import Card from '../components/Card.jsx'; import CompanyLogo from '../components/CompanyLogo.jsx'; import NewsAssets from '../components/NewsAssets.svelte'; import NewsTitle from '../components/NewsTitle.vue'; + export const layout = 'layouts/main.hmx'; + import news from '../data/news.json'; import users from '../data/users.json'; - export function setup({ findContent }) { + export function setup({ context, request }) { + console.log(request); return { - title: 'Community & News', - description: "Snowpack community news and companies that use Snowpack.", - layout: 'layouts/main.hmx', - // Using Snowpack? Want to be featured on snowpack.dev? - // Add your project, organization, or company to the end of this list! - props: { + context: { + title: 'Community & News', + description: "Snowpack community news and companies that use Snowpack.", + // Using Snowpack? Want to be featured on snowpack.dev? + // Add your project, organization, or company to the end of this list! news, users, } - }; + } } </script> -<head> </head> +<slot:head> </slot:head> -<body> - <NewsTitle title={title} /> +<slot:body> + <NewsTitle title={context.title} /> <p> Get the latest news, blog posts, and tutorials on Snowpack. <a href="/feed.xml">Also available via RSS.</a> @@ -45,7 +47,8 @@ working on!</div> </article> - {props.news.reverse().map((item) => <Card:dynamic item={item} />)} + {context.news.reverse().map((item) => + <Card:dynamic item={item} />)} </div> <div class="content"> @@ -54,7 +57,8 @@ <div class="company-logos"> - {props.users.map((user) => <CompanyLogo user={user} />)} + {context.users.map((user) => + <CompanyLogo user={user} />)} <a href="https://github.com/snowpackjs/snowpack/edit/main/www/_template/news.md" target="_blank" title="Add Your Project/Company!" class="add-company-button"> @@ -70,4 +74,4 @@ <NewsAssets /> </div> -</body>
\ No newline at end of file +</slot:body>
\ No newline at end of file diff --git a/examples/snowpack/astro/pages/plugins.hmx b/examples/snowpack/astro/pages/plugins.hmx index 6bda1ebda..8749f0169 100644 --- a/examples/snowpack/astro/pages/plugins.hmx +++ b/examples/snowpack/astro/pages/plugins.hmx @@ -1,29 +1,24 @@ -<script hmx="setup"> - import news from '../data/news.json'; - import users from '../data/users.json'; +<script astro> import PluginSearchPage from '../components/PluginSearchPage.jsx'; - export function setup({ findContent }) { + export const layout = 'layouts/main.hmx'; + + export function setup({ context }) { return { - title: 'The Snowpack Plugin Catalog', - description: 'Snowpack plugins allow for configuration-minimal tooling integration.', - layout: 'layouts/main.hmx', - // Using Snowpack? Want to be featured on snowpack.dev? - // Add your project, organization, or company to the end of this list! - props: { - news, - users, + context: { + title: 'The Snowpack Plugin Catalog', + description: 'Snowpack plugins allow for configuration-minimal tooling integration.', } }; } </script> -<head> -</head> - -<body> +<slot:head> +</slot:head> + +<slot:body> <h2 class="content-title"> - { title } + { context.title } </h2> <h3 class="pluginPage-subheading">Customize Snowpack with optimized build plugins.</h3> @@ -33,4 +28,4 @@ </p> <PluginSearchPage:dynamic /> -</body>
\ No newline at end of file +</slot:body>
\ No newline at end of file diff --git a/examples/snowpack/astro/pages/proof-of-concept-dynamic/[slug].hmx b/examples/snowpack/astro/pages/proof-of-concept-dynamic/[slug].hmx index 0535f4625..55ea99f75 100644 --- a/examples/snowpack/astro/pages/proof-of-concept-dynamic/[slug].hmx +++ b/examples/snowpack/astro/pages/proof-of-concept-dynamic/[slug].hmx @@ -1,4 +1,4 @@ -<script hmx="setup"> +<script astro> import Subnav from '../components/Subnav.hmx'; import { content as Menu } from '../components/Menu.hmx'; // import contentful from 'skypack:contentful'; @@ -16,12 +16,12 @@ <Menu /> </aside> - <Subnav title={title} /> + <Subnav title={context.title} headers={context.content.headers} /> <article class="snow-view-main"> <div class="content"> <h2 class="content-title"> - {title} + {context.title} </h2> <div class="content-layout"> <div class="content-body"> diff --git a/package-lock.json b/package-lock.json index 1d4c4cb77..3e3ac71ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -167,6 +167,12 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz", "integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==" }, + "@types/github-slugger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz", + "integrity": "sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g==", + "dev": true + }, "@types/json-schema": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", @@ -1562,6 +1568,21 @@ "pump": "^3.0.0" } }, + "github-slugger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", + "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==", + "requires": { + "emoji-regex": ">=6.0.0 <=6.1.1" + }, + "dependencies": { + "emoji-regex": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=" + } + } + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", diff --git a/package.json b/package.json index 0dd8b04f9..9681b06f1 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "deepmerge": "^4.2.2", "domhandler": "^4.0.0", "es-module-lexer": "^0.4.1", + "github-slugger": "^1.3.0", "gray-matter": "^4.0.2", "htmlparser2": "^6.0.0", "kleur": "^4.1.4", @@ -54,6 +55,7 @@ "yargs-parser": "^20.2.7" }, "devDependencies": { + "@types/github-slugger": "^1.3.0", "@types/sass": "^1.16.0", "@types/yargs-parser": "^20.2.0", "@typescript-eslint/eslint-plugin": "^4.18.0", diff --git a/snowpack-plugin.cjs b/snowpack-plugin.cjs index 82be28b13..969b6075e 100644 --- a/snowpack-plugin.cjs +++ b/snowpack-plugin.cjs @@ -12,58 +12,10 @@ module.exports = function (snowpackConfig, { resolve } = {}) { output: ['.js'], }, async load({ filePath }) { - const { compilePage, compileComponent } = await transformPromise; + const { compileComponent } = await transformPromise; const projectRoot = snowpackConfig.root; const contents = await readFile(filePath, 'utf-8'); - - if (!filePath.includes('/pages/') && !filePath.includes('/layouts/')) { const result = await compileComponent(contents, { compileOptions: { resolve }, filename: filePath, projectRoot }); - return result.contents; - } - const result = await compilePage(contents, { - compileOptions: { resolve }, - filename: filePath, - projectRoot, - }); - - try { - return /* js */ ` - ${result.contents} - - export default async (childDatas, childRenderFns) => { - // Kind of hacky, can clean up if this works - const renderHmx = {setup, head, body}; - const merge = (await import('deepmerge')).default; - const content = childDatas && childDatas[0].content; - const _data = await renderHmx.setup({content}); - if (_data.layout) { - const renderLayout = (await import('/_hmx/layouts/' + _data.layout.replace(/.*layouts\\//, "").replace(/\.hmx$/, '.js'))).default; - return renderLayout( - [...(childDatas || []), _data], - [...(childRenderFns || []), renderHmx] - ); - } - const data = merge.all([_data, ...(childDatas || [])]); - let headResult; - let bodyResult; - for (const renderFn of (childRenderFns || [])) { - let headAndBody = await Promise.all([ - renderFn.head(data, headResult), - renderFn.body(data, bodyResult) - ]); - headResult = headAndBody[0]; - bodyResult = headAndBody[1]; - } - return h(Fragment, null, [ - renderHmx.head(data, headResult, true), - renderHmx.body(data, bodyResult, true), - ]); - }; - `; - } catch (err) { - console.error(err); - } - return result.contents; }, }; diff --git a/src/@types/astro.ts b/src/@types/astro.ts index f7170cb61..d2d82f3aa 100644 --- a/src/@types/astro.ts +++ b/src/@types/astro.ts @@ -17,9 +17,12 @@ export interface JsxItem { export interface TransformResult { script: string; + head: JsxItem | undefined; + body: JsxItem | undefined; items: JsxItem[]; } export interface CompileResult { + result: TransformResult; contents: string; } diff --git a/src/codegen/index.ts b/src/codegen/index.ts index 662d63858..0b94fdfd3 100644 --- a/src/codegen/index.ts +++ b/src/codegen/index.ts @@ -53,6 +53,10 @@ function getAttributes(attrs: Attribute[]): Record<string, string> { continue; } const val: TemplateNode = attr.value[0]; + if (!val) { + result[attr.name] = '(' + val + ')'; + continue; + } switch (val.type) { case 'MustacheTag': result[attr.name] = '(' + val.expression + ')'; @@ -143,33 +147,37 @@ function getComponentWrapper(_name: string, { type, url }: { type: string; url: } function compileScriptSafe(raw: string, loader: 'jsx' | 'tsx'): string { + let compiledCode = compileExpressionSafe(raw, loader); // esbuild treeshakes unused imports. In our case these are components, so let's keep them. const imports = eslexer - .parse(raw)[0] - .filter(({ d }) => d === -1) - .map((i: any) => raw.substring(i.ss, i.se)); + .parse(raw)[0] + .filter(({ d }) => d === -1) + .map((i) => raw.substring(i.ss, i.se)); + for (let importStatement of imports) { + if (!compiledCode.includes(importStatement)) { + compiledCode = importStatement + '\n' + compiledCode; + } + } + return compiledCode; +} + +function compileExpressionSafe(raw: string, loader: 'jsx' | 'tsx'): string { let { code } = transformSync(raw, { loader, jsxFactory: 'h', jsxFragment: 'Fragment', charset: 'utf8', }); - - for (let importStatement of imports) { - if (!code.includes(importStatement)) { - code = importStatement + '\n' + code; - } - } - return code; + } export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Promise<TransformResult> { await eslexer.init; // Compile scripts as TypeScript, always - const script = compileScriptSafe(ast.instance ? ast.instance.content : '', 'tsx'); + const script = compileScriptSafe(ast.module ? ast.module.content : '', 'tsx'); // Todo: Validate that `h` and `Fragment` aren't defined in the script const [scriptImports] = eslexer.parse(script, 'optional-sourcename'); @@ -182,6 +190,8 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro ); const additionalImports = new Set<string>(); + let headItem: JsxItem | undefined; + let bodyItem: JsxItem | undefined; let items: JsxItem[] = []; let mode: 'JSX' | 'SCRIPT' | 'SLOT' = 'JSX'; let collectionItem: JsxItem | undefined; @@ -192,7 +202,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro enter(node: TemplateNode) { switch (node.type) { case 'MustacheTag': - let code = compileScriptSafe(node.expression, 'jsx'); + let code = compileExpressionSafe(node.expression, 'jsx'); let matches: RegExpExecArray[] = []; let match: RegExpExecArray | null | undefined; @@ -230,8 +240,12 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro return; } break; + + case 'Head': + case 'Body': case 'InlineComponent': - case 'Element': + case 'Title': + case 'Element': { const name: string = node.name; if (!name) { throw new Error('AHHHH'); @@ -241,6 +255,16 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro currentItemName = name; if (!collectionItem) { collectionItem = { name, jsx: '' }; + if (node.type === 'Head') { + collectionItem.jsx += `h(Fragment, null`; + headItem = collectionItem; + return; + } + if (node.type === 'Body') { + collectionItem.jsx += `h(Fragment, null`; + bodyItem = collectionItem; + return; + } items.push(collectionItem); } collectionItem.jsx += collectionItem.jsx === '' ? '' : ','; @@ -249,10 +273,6 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro collectionItem.jsx += `h("${name}", ${attributes ? generateAttributes(attributes) : 'null'}`; return; } - if (name === 'Component') { - collectionItem.jsx += `h(Fragment, null`; - return; - } const [componentName, componentKind] = name.split(':'); const componentImportData = components[componentName]; if (!componentImportData) { @@ -265,6 +285,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro collectionItem.jsx += `h(${wrapper}, ${attributes ? generateAttributes(attributes) : 'null'}`; return; + } case 'Attribute': { this.skip(); return; @@ -293,7 +314,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro return; } default: - throw new Error('Unexpected node type: ' + node.type); + throw new Error('Unexpected (enter) node type: ' + node.type); } }, leave(node, parent, prop, index) { @@ -314,6 +335,9 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro if (!collectionItem) { return; } + case 'Head': + case 'Body': + case 'Title': case 'Element': case 'InlineComponent': if (!collectionItem) { @@ -329,13 +353,15 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro return; } default: - throw new Error('Unexpected node type: ' + node.type); + throw new Error('Unexpected (leave) node type: ' + node.type); } }, }); return { script: script + '\n' + Array.from(additionalImports).join('\n'), + head: headItem, + body: bodyItem, items, }; } diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index bedb29cda..b77357d23 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -58,7 +58,7 @@ export interface Parser { export interface Script extends BaseNode { type: 'Script'; - context: string; + context: 'runtime' | 'setup'; content: string; } @@ -75,8 +75,8 @@ export interface Style extends BaseNode { export interface Ast { html: TemplateNode; css: Style; - instance: Script; module: Script; + // instance: Script; } export interface Warning { @@ -94,38 +94,6 @@ export type ModuleFormat = 'esm' | 'cjs'; export type CssHashGetter = (args: { name: string; filename: string | undefined; css: string; hash: (input: string) => string }) => string; -export interface CompileOptions { - format?: ModuleFormat; - name?: string; - filename?: string; - generate?: 'dom' | 'ssr' | false; - - sourcemap?: object | string; - outputFilename?: string; - cssOutputFilename?: string; - sveltePath?: string; - - dev?: boolean; - accessors?: boolean; - immutable?: boolean; - hydratable?: boolean; - legacy?: boolean; - customElement?: boolean; - tag?: string; - css?: boolean; - loopGuardTimeout?: number; - namespace?: string; - cssHash?: CssHashGetter; - - preserveComments?: boolean; - preserveWhitespace?: boolean; -} - -export interface ParserOptions { - filename?: string; - customElement?: boolean; -} - export interface Visitor { enter: (node: Node) => void; leave?: (node: Node) => void; diff --git a/src/compiler/parse/index.ts b/src/compiler/parse/index.ts index eab2c42c5..f98119d73 100644 --- a/src/compiler/parse/index.ts +++ b/src/compiler/parse/index.ts @@ -232,33 +232,34 @@ export default function parse(template: string, options: ParserOptions = {}): As ); } - const instance_scripts = parser.js.filter((script) => script.context === 'default'); - const module_scripts = parser.js.filter((script) => script.context === 'module'); + // const instance_scripts = parser.js.filter((script) => script.context === 'default'); + // const module_scripts = parser.js.filter((script) => script.context === 'module'); + const hmx_scripts = parser.js.filter((script) => script.context === 'setup'); - if (instance_scripts.length > 1) { + if (hmx_scripts.length > 1) { parser.error( { code: 'invalid-script', - message: 'A component can only have one instance-level <script> element', + message: 'A component can only have one <script astro> element', }, - instance_scripts[1].start + hmx_scripts[1].start ); } - if (module_scripts.length > 1) { - parser.error( - { - code: 'invalid-script', - message: 'A component can only have one <script context="module"> element', - }, - module_scripts[1].start - ); - } + // if (module_scripts.length > 1) { + // parser.error( + // { + // code: 'invalid-script', + // message: 'A component can only have one <script context="module"> element', + // }, + // module_scripts[1].start + // ); + // } return { html: parser.html, css: parser.css[0], - instance: instance_scripts[0], - module: module_scripts[0], + // instance: instance_scripts[0], + module: hmx_scripts[0], }; } diff --git a/src/compiler/parse/read/script.ts b/src/compiler/parse/read/script.ts index eb7a8c5b3..7afbfb08f 100644 --- a/src/compiler/parse/read/script.ts +++ b/src/compiler/parse/read/script.ts @@ -7,15 +7,16 @@ import { Node, Program } from 'estree'; const script_closing_tag = '</script>'; -function get_context(parser: Parser, attributes: any[], start: number): string { - const context = attributes.find((attribute) => attribute.name === 'context'); - if (!context) return 'default'; +function get_context(parser: Parser, attributes: any[], start: number): 'runtime' | 'setup' { + const context = attributes.find((attribute) => attribute.name === 'astro'); + if (!context) return 'runtime'; + if (context.value === true) return 'setup'; if (context.value.length !== 1 || context.value[0].type !== 'Text') { parser.error( { code: 'invalid-script', - message: 'context attribute must be static', + message: 'astro attribute must be static', }, start ); @@ -23,11 +24,11 @@ function get_context(parser: Parser, attributes: any[], start: number): string { const value = context.value[0].data; - if (value !== 'module') { + if (value !== 'setup') { parser.error( { code: 'invalid-script', - message: 'If the context attribute is supplied, its value must be "module"', + message: 'If the "astro" attribute has a value, its value must be "setup"', }, context.start ); diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index fa5ccb6e3..e1dbcab42 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -14,13 +14,14 @@ import list from '../../utils/list.js'; const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const meta_tags = new Map([ - ['svelte:head', 'Head'], - ['svelte:options', 'Options'], - ['svelte:window', 'Window'], - ['svelte:body', 'Body'], + ['slot:head', 'Head'], + ['slot:body', 'Body'], + // ['astro:options', 'Options'], + // ['astro:window', 'Window'], + // ['astro:body', 'Body'], ]); -const valid_meta_tags = Array.from(meta_tags.keys()).concat('svelte:self', 'svelte:component', 'svelte:fragment'); +const valid_meta_tags = Array.from(meta_tags.keys()); //.concat('astro:self', 'astro:component', 'astro:fragment'); const specials = new Map([ [ @@ -39,9 +40,10 @@ const specials = new Map([ ], ]); -const SELF = /^svelte:self(?=[\s/>])/; -const COMPONENT = /^svelte:component(?=[\s/>])/; -const SLOT = /^svelte:fragment(?=[\s/>])/; +const SELF = /^astro:self(?=[\s/>])/; +const COMPONENT = /^astro:component(?=[\s/>])/; +const SLOT = /^astro:fragment(?=[\s/>])/; +const HEAD = /^head(?=[\s/>])/; function parent_is_head(stack) { let i = stack.length; @@ -79,7 +81,7 @@ export default function tag(parser: Parser) { if (meta_tags.has(name)) { const slug = meta_tags.get(name).toLowerCase(); if (is_closing_tag) { - if ((name === 'svelte:window' || name === 'svelte:body') && parser.current().children.length) { + if ((name === 'astro:window' || name === 'astro:body') && parser.current().children.length) { parser.error( { code: `invalid-${slug}-content`, @@ -115,9 +117,9 @@ export default function tag(parser: Parser) { const type = meta_tags.has(name) ? meta_tags.get(name) - : /[A-Z]/.test(name[0]) || name === 'svelte:self' || name === 'svelte:component' + : /[A-Z]/.test(name[0]) || name === 'astro:self' || name === 'astro:component' ? 'InlineComponent' - : name === 'svelte:fragment' + : name === 'astro:fragment' ? 'SlotTemplate' : name === 'title' && parent_is_head(parser.stack) ? 'Title' @@ -197,13 +199,13 @@ export default function tag(parser: Parser) { parser.allow_whitespace(); } - if (name === 'svelte:component') { + if (name === 'astro:component') { const index = element.attributes.findIndex((attr) => attr.type === 'Attribute' && attr.name === 'this'); if (!~index) { parser.error( { code: 'missing-component-definition', - message: "<svelte:component> must have a 'this' attribute", + message: "<astro:component> must have a 'this' attribute", }, start ); @@ -281,27 +283,29 @@ function read_tag_name(parser: Parser) { parser.error( { code: 'invalid-self-placement', - message: '<svelte:self> components can only exist inside {#if} blocks, {#each} blocks, or slots passed to components', + message: '<astro:self> components can only exist inside {#if} blocks, {#each} blocks, or slots passed to components', }, start ); } - return 'svelte:self'; + return 'astro:self'; } - if (parser.read(COMPONENT)) return 'svelte:component'; + if (parser.read(COMPONENT)) return 'astro:component'; - if (parser.read(SLOT)) return 'svelte:fragment'; + if (parser.read(SLOT)) return 'astro:fragment'; + + if (parser.read(HEAD)) return 'head'; const name = parser.read_until(/(\s|\/|>)/); if (meta_tags.has(name)) return name; - if (name.startsWith('svelte:')) { + if (name.startsWith('astro:')) { const match = fuzzymatch(name.slice(7), valid_meta_tags); - let message = `Valid <svelte:...> tag names are ${list(valid_meta_tags)}`; + let message = `Valid <astro:...> tag names are ${list(valid_meta_tags)}`; if (match) message += ` (did you mean '${match}'?)`; parser.error( diff --git a/src/generate.ts b/src/generate.ts index ad2e9ded9..225bf4fde 100644 --- a/src/generate.ts +++ b/src/generate.ts @@ -52,6 +52,7 @@ export default async function (astroConfig: AstroConfig) { await writeFile(outPath, html, 'utf-8'); } catch (err) { console.error('Unable to generate page', rel); + console.error(err); } } diff --git a/src/markdown-encode.ts b/src/markdown-encode.ts deleted file mode 100644 index 173c63fde..000000000 --- a/src/markdown-encode.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { HtmlExtension, Token } from 'micromark/dist/shared-types'; - -const characterReferences = { - '"': 'quot', - '&': 'amp', - '<': 'lt', - '>': 'gt', - '{': 'lbrace', - '}': 'rbrace', -}; - -type EncodedChars = '"' | '&' | '<' | '>' | '{' | '}'; - -function encode(value: string): string { - return value.replace(/["&<>{}]/g, (raw: string) => { - return '&' + characterReferences[raw as EncodedChars] + ';'; - }); -} - -const plugin: HtmlExtension = { - exit: { - codeFlowValue() { - const token: Token = arguments[0]; - const serialize = (this.sliceSerialize as unknown) as (t: Token) => string; - const raw = (this.raw as unknown) as (s: string) => void; - const value = serialize(token); - raw(encode(value)); - }, - }, -}; - -export { plugin as default }; diff --git a/src/micromark-collect-headers.ts b/src/micromark-collect-headers.ts new file mode 100644 index 000000000..d614cc5b4 --- /dev/null +++ b/src/micromark-collect-headers.ts @@ -0,0 +1,35 @@ +import slugger from 'github-slugger'; + +// NOTE: micromark has terrible TS types. Instead of fighting with the +// limited/broken TS types that they ship, we just reach for our good friend, "any". +export function createMarkdownHeadersCollector() { + const headers: any[] = []; + let currentHeader: any; + return { + headers, + headersExtension: { + enter: { + atxHeading(node: any) { + currentHeader = {}; + headers.push(currentHeader); + }, + atxHeadingSequence(node: any) { + currentHeader.depth = this.sliceSerialize(node).length; + }, + atxHeadingText(node: any) { + currentHeader.text = this.sliceSerialize(node); + }, + } as any, + exit: { + atxHeading(node: any) { + currentHeader.slug = slugger.slug(currentHeader.text); + this.tag(`<h${currentHeader.depth} id="${currentHeader.slug}">`); + this.raw(currentHeader.text); + this.tag(`</h${currentHeader.depth}>`); + + // console.log(this.sliceSerialize(node)); + }, + } as any, + } as any, + }; +} diff --git a/src/runtime.ts b/src/runtime.ts index a17b552e8..91ee9c5d2 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -58,7 +58,14 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro try { const mod = await snowpackRuntime.importModule(selectedPageUrl); - const html = (await mod.exports.default()) as string; + const html = (await mod.exports.__renderPage({ + request: { + host: fullurl.hostname, + path: fullurl.pathname, + href: fullurl.toString(), + }, + children: [], + })) as string; return { statusCode: 200, diff --git a/src/transform2.ts b/src/transform2.ts index 0ccdc6b55..4cca58510 100644 --- a/src/transform2.ts +++ b/src/transform2.ts @@ -7,7 +7,7 @@ import matter from 'gray-matter'; import gfmHtml from 'micromark-extension-gfm/html.js'; import { CompileResult, TransformResult } from './@types/astro'; import { parse } from './compiler/index.js'; -import markdownEncode from './markdown-encode.js'; +import { createMarkdownHeadersCollector } from './micromark-collect-headers.js'; import { defaultLogOptions } from './logger.js'; import { optimize } from './optimize/index.js'; import { codegen } from './codegen/index.js'; @@ -51,33 +51,35 @@ async function convertMdToJsx( contents: string, { compileOptions, filename, fileID }: { compileOptions: CompileOptions; filename: string; fileID: string } ): Promise<TransformResult> { - // This doesn't work. const { data: _frontmatterData, content } = matter(contents); + const {headers, headersExtension} = createMarkdownHeadersCollector(); const mdHtml = micromark(content, { extensions: [gfmSyntax()], - htmlExtensions: [gfmHtml, markdownEncode], + htmlExtensions: [gfmHtml, headersExtension], }); - const setupData = { - title: _frontmatterData.title, - description: _frontmatterData.description, - layout: _frontmatterData.layout, + console.log("headers", headers); + const setupContext = { + ..._frontmatterData, content: { frontmatter: _frontmatterData, - - // This is an awful hack due to Svelte parser disliking script tags badly. - source: content.replace(/<\/?script/g, '<SCRIPT'), + headers, + source: content, html: mdHtml, }, - props: { - ..._frontmatterData, - }, }; + // </script> can't be anywhere inside of a JS string, otherwise the HTML parser fails. + // Break it up here so that the HTML parser won't detect it. + const stringifiedSetupContext = JSON.stringify(setupContext).replace(/\<\/script\>/g, `</scrip" + "t>`); + return convertHmxToJsx( - `<script hmx="setup">export function setup() { - return ${JSON.stringify(setupData)}; - }</script><head></head><body>${mdHtml}</body>`, + `<script astro> + ${_frontmatterData.layout ? `export const layout = ${JSON.stringify(_frontmatterData.layout)};` : ''} + export function setup({context}) { + return {context: ${stringifiedSetupContext} }; + } + </script><slot:head></slot:head><slot:body><section>{${JSON.stringify(mdHtml)}}</section></slot:body>`, { compileOptions, filename, fileID } ); } @@ -97,49 +99,85 @@ async function transformFromSource( } } -export async function compilePage( +export async function compileComponent( source: string, { compileOptions = defaultCompileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string } ): Promise<CompileResult> { const sourceJsx = await transformFromSource(source, { compileOptions, filename, projectRoot }); + const headItem = sourceJsx.head; + const bodyItem = sourceJsx.body; + const headItemJsx = !headItem ? 'null' : headItem.jsx; + const bodyItemJsx = !bodyItem ? 'null' : bodyItem.jsx; - const headItem = sourceJsx.items.find((item) => item.name === 'head'); - const bodyItem = sourceJsx.items.find((item) => item.name === 'body'); - const headItemJsx = !headItem ? 'null' : headItem.jsx.replace('"head"', 'isRoot ? "head" : Fragment'); - const bodyItemJsx = !bodyItem ? 'null' : bodyItem.jsx.replace('"head"', 'isRoot ? "body" : Fragment'); + // sort <style> tags first + // TODO: remove these and inject in <head> + sourceJsx.items.sort((a, b) => (a.name === 'style' && b.name !== 'style' ? -1 : 0)); - const modJsx = ` + // return template + let modJsx = ` +// <script astro></script> ${sourceJsx.script} +// \`__render()\`: Render the contents of the HMX module. "<slot:*>" elements are not +// included (see below). import { h, Fragment } from '${internalImport('h.js')}'; -export function head({title, description, props}, child, isRoot) { return (${headItemJsx}); } -export function body({title, description, props}, child, isRoot) { return (${bodyItemJsx}); } -`.trim(); - - return { - contents: modJsx, +export default function __render(props) { return h(Fragment, null, ${sourceJsx.items.map(({ jsx }) => jsx).join(',')}); } + +// <slot:*> render functions +export function __slothead(context, child) { return h(Fragment, null, ${headItemJsx}); } +export function __slotbody(context, child) { return h(Fragment, null, ${bodyItemJsx}); } +`; + + if (headItemJsx || bodyItemJsx) { + modJsx += ` +// \`__renderPage()\`: Render the contents of the HMX module as a page. This is a special flow, +// triggered by loading a component directly by URL. +// If the page exports a defined "layout", then load + render those first. "context", "slot:head", +// and "slot:body" should all inherit from parent layouts, merging together in the correct order. +export async function __renderPage({request, children}) { + const currentChild = { + __slothead, + __slotbody, + setup: typeof setup === 'undefined' ? (passthrough) => passthrough : setup, + layout: typeof layout === 'undefined' ? undefined : layout, }; -} - -export async function compileComponent( - source: string, - { compileOptions = defaultCompileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string } -): Promise<CompileResult> { - const sourceJsx = await transformFromSource(source, { compileOptions, filename, projectRoot }); - - // throw error if <Component /> missing - if (!sourceJsx.items.find(({ name }) => name === 'Component')) throw new Error(`${filename} <Component> expected!`); - // sort <style> tags first - // TODO: remove these and inject in <head> - sourceJsx.items.sort((a, b) => (a.name === 'style' && b.name !== 'style' ? -1 : 0)); + // find all layouts, going up the layout chain. + if (currentChild.layout) { + const layoutComponent = (await import('/_hmx/layouts/' + layout.replace(/.*layouts\\//, "").replace(/\.hmx$/, '.js'))); + return layoutComponent.__renderPage({ + request, + children: [currentChild, ...children], + }); + } + + const isRoot = true; + const merge = (await import('deepmerge')).default; + + // call all children setup scripts, in order, and return. + let mergedContext = {}; + for (const child of [currentChild, ...children]) { + const childSetupResult = await child.setup({request, context: mergedContext}); + mergedContext = childSetupResult.context ? merge(mergedContext, childSetupResult.context) : mergedContext; + } + + Object.freeze(mergedContext); + + let headResult; + let bodyResult; + for (const child of children.reverse()) { + headResult = await child.__slothead(mergedContext, headResult); + bodyResult = await child.__slotbody(mergedContext, bodyResult); + } + return h(Fragment, null, [ + h("head", null, currentChild.__slothead(mergedContext, headResult)), + h("body", null, currentChild.__slotbody(mergedContext, bodyResult)), + ]); +};\n`; + } - // return template - const modJsx = ` - import { h, Fragment } from '${internalImport('h.js')}'; - export default function(props) { return h(Fragment, null, ${sourceJsx.items.map(({ jsx }) => jsx).join(',')}); } - `.trim(); return { + result: sourceJsx, contents: modJsx, }; } diff --git a/test/fixtures/hmx-basic/astro/pages/index.hmx b/test/fixtures/hmx-basic/astro/pages/index.hmx index a8ea2678a..035760ddb 100644 --- a/test/fixtures/hmx-basic/astro/pages/index.hmx +++ b/test/fixtures/hmx-basic/astro/pages/index.hmx @@ -1,4 +1,4 @@ -<script hmx="setup"> +<script astro> export function setup() { return { props: {} @@ -6,10 +6,10 @@ } </script> -<head> +<slot:head> <!-- Head Stuff --> -</head> +</slot:head> -<body> +<slot:body> <h1>Hello world!</h1> -</body>
\ No newline at end of file +</slot:body>
\ No newline at end of file |