lots of improvements

This commit is contained in:
Fred K. Schott 2021-03-21 00:44:42 -07:00
parent 2082001ff8
commit 417657f138
31 changed files with 514 additions and 476 deletions

View file

@ -1,12 +1,10 @@
<Component> <section class="grid-banner">
<section class="grid-banner"> <div class="notification">
<div class="notification"> <div class="container">
<div class="container"> Snowpack 3.0 is out now!
Snowpack 3.0 is out now! <a href="/posts/2021-01-13-snowpack-3-0">
<a href="/posts/2021-01-13-snowpack-3-0"> Read the announcement post →
Read the announcement post → </a>
</a>
</div>
</div> </div>
</section> </div>
</Component> </section>

View file

@ -1,5 +1,4 @@
<Component> <div class="hero">
<div class="hero">
<div class="section"> <div class="section">
<h1 class="header-snowpack">Snowpack</h1> <h1 class="header-snowpack">Snowpack</h1>
<p class="header-snowpack-subtitle">The faster frontend build tool.</p> <p class="header-snowpack-subtitle">The faster frontend build tool.</p>
@ -7,11 +6,15 @@
<div class="hero-cta"> <div class="hero-cta">
<a href="/tutorials/quick-start" class="button button-primary">Get started</a> <a href="/tutorials/quick-start" class="button button-primary">Get started</a>
<div style="margin: 0.5rem"></div> <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 class="faded" style="margin-right: 0.75rem;">$</span>
<span id="copy-button-text">npm install snowpack</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"> <svg style="height: 22px;width: 22px;margin: -0.1rem -0.1rem 0 0.75rem;" aria-hidden="true" focusable="false"
<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> 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> </svg>
</button> </button>
<script type="module"> <script type="module">
@ -19,10 +22,10 @@
const clippy = new Clipboard('.copy-button'); const clippy = new Clipboard('.copy-button');
const copyText = document.getElementById('copy-button').innerHTML; const copyText = document.getElementById('copy-button').innerHTML;
clippy.on('success', function (e) { 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); console.info('Trigger:', e);
document.getElementById('copy-button').innerHTML = '<span>copied to clipboard!</span>'; 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; document.getElementById('copy-button').innerHTML = copyText;
}, false); }, false);
@ -39,9 +42,9 @@
<div class="hero-cta"> <div class="hero-cta">
<!-- Place this tag where you want the button to render. --> <!-- Place this tag where you want the button to render. -->
<div class="hidden-mobile" style="text-align: center; height: 36px;"> <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>
</div> </div>
</div> </div>
</Component>

View file

@ -1,4 +1,3 @@
<Component>
<nav class="snow-toc"> <nav class="snow-toc">
<ol class="snow-toc-contents"> <ol class="snow-toc-contents">
<li class="snow-toc-section"> <li class="snow-toc-section">
@ -83,4 +82,3 @@
</li> </li>
</ol> </ol>
</nav> </nav>
</Component>

View file

@ -1,21 +1,27 @@
<Component>
<nav class="snow-nav"> <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"> <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> <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> </svg>
</button> </button>
<a class="snow-nav-logo snow-logo" href="/"> <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"> <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> </g>
</svg> </svg>
<span class="snow-logo-type">Snowpack</span> <span class="snow-logo-type">Snowpack</span>
</a> </a>
<div class="search-form"> <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="search-form-hint">
<span class="sr-only">Press </span> <span class="sr-only">Press </span>
<kbd class="font-sans"><abbr title="Command" style="text-decoration: none;">⌘</abbr></kbd> <kbd class="font-sans"><abbr title="Command" style="text-decoration: none;">⌘</abbr></kbd>
@ -29,64 +35,75 @@
{`v${props.version}`} {`v${props.version}`}
</a> </a>
<a href="https://github.com/snowpackjs/snowpack" target="_blank" class="snow-nav-link snow-nav-link__desktop"> <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"> <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="snow-icon" role="img"
<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> 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> </svg>
</a> </a>
<a href="https://twitter.com/snowpackjs" target="_blank" class="snow-nav-link snow-nav-link__desktop"> <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"> <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" class="snow-icon" role="img"
<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> 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> </svg>
</a> </a>
<a href="https://discord.gg/snowpack" target="_blank" class="snow-nav-link snow-nav-link__desktop"> <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> </a>
</nav> </nav>
<script> <script>
function handleMobileNav(evt) { function handleMobileNav(evt) {
evt.preventDefault(); evt.preventDefault();
/*If hidden-mobile class is enabled that means we are on desktop do overflow normal but we /*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 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 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 body postion should be unset
*/ */
document.body.classList.toggle('is-nav-open'); document.body.classList.toggle('is-nav-open');
const isOpen = document.body.classList.contains('is-nav-open'); const isOpen = document.body.classList.contains('is-nav-open');
if (isOpen) { if (isOpen) {
evt.target.setAttribute('aria-expanded', 'true'); evt.target.setAttribute('aria-expanded', 'true');
} else { } else {
evt.target.setAttribute('aria-expanded', 'false'); evt.target.setAttribute('aria-expanded', 'false');
}
} }
}
const mobileNavBtn = document.getElementById('toc-drawer-button'); const mobileNavBtn = document.getElementById('toc-drawer-button');
mobileNavBtn.addEventListener('click', handleMobileNav); mobileNavBtn.addEventListener('click', handleMobileNav);
mobileNavBtn.addEventListener('touchend', handleMobileNav); mobileNavBtn.addEventListener('touchend', handleMobileNav);
if (window.location.pathname.startsWith('/posts')) { if (window.location.pathname.startsWith('/posts')) {
mobileNavBtn.style.display = 'none'; mobileNavBtn.style.display = 'none';
} }
const searchFormInputEl = document.getElementById('search-form-input'); const searchFormInputEl = document.getElementById('search-form-input');
searchFormInputEl.addEventListener('keyup', () => { searchFormInputEl.addEventListener('keyup', () => {
const gridTocEl = document.querySelector('#nav-primary'); const gridTocEl = document.querySelector('#nav-primary');
if (searchFormInputEl.value) { if (searchFormInputEl.value) {
gridTocEl.classList.add('is-mobile-hidden'); gridTocEl.classList.add('is-mobile-hidden');
} else { } else {
gridTocEl.classList.remove('is-mobile-hidden'); gridTocEl.classList.remove('is-mobile-hidden');
} }
}); });
document.onkeydown = function (e) { document.onkeydown = function (e) {
if ((e.ctrlKey || e.metaKey) && e.which == 75) { if ((e.ctrlKey || e.metaKey) && e.which == 75) {
e.preventDefault(); e.preventDefault();
searchFormInputEl.focus(); searchFormInputEl.focus();
} }
}; };
</script> </script>
<script type="module" defer> <script type="module" defer>
import docsearch from 'docsearch.js/dist/cdn/docsearch.min.js'; import docsearch from 'docsearch.js/dist/cdn/docsearch.min.js';
docsearch({ docsearch({
apiKey: '562139304880b94536fc53f5d65c5c19', indexName: 'snowpack', inputSelector: '.search-form-input', debug: true // Set debug to true if you want to inspect the dropdown apiKey: '562139304880b94536fc53f5d65c5c19', indexName: 'snowpack', inputSelector: '.search-form-input', debug: true // Set debug to true if you want to inspect the dropdown
}); });
</script> </script>
</Component>

View file

@ -1,19 +1,21 @@
<Component> <script type="module" defer src="/js/index.js"></script>
<script type="module" defer src="/js/index.js"></script> <aside class="snow-toc snow-toc__subnav snow-view-subnav">
<aside class="snow-toc snow-toc__subnav snow-view-subnav"> <h2 class="content-title">
<h2 class="content-title"> {props.title}
{props.title} </h2>
</h2>
{props.headers.length > 0 && <div>
<h4 class="snow-toc-section-header">On this page</h4> <h4 class="snow-toc-section-header">On this page</h4>
<nav class="toc"> <nav class="toc">
<ol> <ol>
{props.headings.map((heading) => { {props.headers.map((heading) => {
return <li><a href={heading.url}>{heading.text}</a></li> return <li><a href={"#" + heading.slug}>{heading.text}</a></li>
})} })}
</ol> </ol>
</nav> </nav>
<hr /> <hr />
<h4 class="snow-toc-section-header">Suggest a change</h4> </div>}
<a href="https://github.com/snowpackjs/snowpack/blob/main/www/{props.inputPath}">Edit this page on GitHub</a>
</aside> <h4 class="snow-toc-section-header">Suggest a change</h4>
</Component> <a href="https://github.com/snowpackjs/snowpack/blob/main/www/{props.inputPath}">Edit this page on GitHub</a>
</aside>

View file

@ -1,56 +1,56 @@
<script hmx="setup"> <script astro>
import Banner from '../components/Banner.hmx'; import Banner from '../components/Banner.hmx';
import Nav from '../components/Nav.hmx'; import Nav from '../components/Nav.hmx';
export function setup() { export function setup({ context }) {
return { return {
title: 'Snowpack', context: {
description: 'Snowpack is a lightning-fast frontend build tool, designed for the modern web.', title: 'Snowpack',
props: { description: 'Snowpack is a lightning-fast frontend build tool, designed for the modern web.',
version: '3.0.13', currentSnowpackVersion: '3.0.13',
} }
}; };
} }
</script> </script>
<head> <slot:head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /> <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="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="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" /> <link rel="manifest" href="/favicon/site.webmanifest" />
<!-- Primary Meta Tags --> <!-- Primary Meta Tags -->
<title>{title}</title> <title>{context.title}</title>
<meta name="title" content={title} /> <meta name="title" content={context.title} />
<meta name="description" content="{description}" /> <meta name="description" content="{context.description}" />
<!-- Open Graph / Facebook --> <!-- Open Graph / Facebook -->
<meta property="og:type" content="website"/> <meta property="og:type" content="website" />
<meta property="og:url" content="https://www.snowpack.dev{props.permalink}"/> <meta property="og:url" content={context.permalink}/>
<meta property="og:title" content={title}/> <meta property="og:title" content={context.title} />
<meta property="og:description" content={description}/> <meta property="og:description" content={context.description} />
<meta property="og:image" content="https://www.snowpack.dev/img/social-2.jpg"/> <meta property="og:image" content="https://www.snowpack.dev/img/social-2.jpg" />
<!-- Twitter --> <!-- Twitter -->
<meta property="twitter:card" content="summary_large_image"/> <meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://www.snowpack.dev{props.permalink}"/> <meta property="twitter:url" content={context.permalink}/>
<meta property="twitter:title" content={title}/> <meta property="twitter:title" content={context.title} />
<meta property="twitter:description" content={description}/> <meta property="twitter:description" content={context.description} />
<meta property="twitter:image" content="https://www.snowpack.dev/img/social-2.jpg"/> <meta property="twitter:image" content="https://www.snowpack.dev/img/social-2.jpg" />
<!-- Global Stylesheets --> <!-- Global Stylesheets -->
<link rel="stylesheet" href="/css/app.css" /> <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. --> <!-- Note: You can then add additional, custom things here as well. -->
<!-- if no slot given, assume add to bottom --> <!-- if no slot given, assume add to bottom -->
<slot></slot> <slot></slot>
</head> </slot:head>
<body class="base 2"> <slot:body>
<Banner></Banner> <Banner></Banner>
<Nav version={props.version} /> <Nav version={context.currentSnowpackVersion} />
<!-- if no slot given, assume add to bottom --> <!-- if no slot given, assume add to bottom -->
<slot></slot> <slot></slot>
@ -65,4 +65,4 @@
gtag('js', new Date()); gtag('js', new Date());
gtag('config', 'UA-130280175-9', { page_path: location.pathname === '/' ? (location.pathname + location.hash) : (location.pathname) }); gtag('config', 'UA-130280175-9', { page_path: location.pathname === '/' ? (location.pathname + location.hash) : (location.pathname) });
</script> </script>
</body> </slot:body>

View file

@ -1,12 +1,14 @@
<script hmx="setup"> <script astro>
import Menu from '../components/Menu.hmx'; import Menu from '../components/Menu.hmx';
import Subnav from '../components/Subnav.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> </script>
<head> <slot:head>
<style> <style>
.cover-wrapper { .cover-wrapper {
width: 100%; width: 100%;
@ -47,13 +49,13 @@
</style> </style>
<slot></slot> <slot></slot>
</head> </slot:head>
<body> <slot:body>
<div class="cover-wrapper"> <div class="cover-wrapper">
<img class="cover-blur" src={props.cover} alt=""/> <img class="cover-blur" src={context.cover} alt=""/>
<img class="cover" src={props.cover} alt=""/> <img class="cover" src={context.cover} alt=""/>
</div> </div>
<div class="container"> <div class="container">
@ -66,7 +68,7 @@
<article class="snow-view-main"> <article class="snow-view-main">
<div class="content"> <div class="content">
<h2 class="content-title"> <h2 class="content-title">
{title} {context.title}
</h2> </h2>
<div class="content-layout"> <div class="content-layout">
<div class="content-body"> <div class="content-body">
@ -76,7 +78,7 @@
</div> </div>
</article> </article>
<Subnav title={title} /> <Subnav title={context.title} headers={context.content.headers} />
</section> </section>
</div> </div>
</body> </slot:body>

View file

@ -1,33 +1,22 @@
<script hmx="setup"> <script astro>
import Subnav from '../components/Subnav.hmx'; import Subnav from '../components/Subnav.hmx';
import Menu from '../components/Menu.hmx'; import Menu from '../components/Menu.hmx';
import markdownToAst from "mdast-util-from-markdown";
import markdownTableOfContents from "mdast-util-toc";
export function setup({content}) { export const layout = 'layouts/base.hmx';
const mdAst = markdownToAst(content.source); export function setup({ context }) {
// This is super gross, and just me trying to get an existing table of contents generator to work return {
// we can either do this internally as helpful sugar, or build a simpler one as a plugin context: {
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,
}
}; };
} }
</script> </script>
<head> <slot:head>
<slot></slot> <slot></slot>
</head> </slot:head>
<body> <slot:body>
<div class="container"> <div class="container">
<section class="snow-view__docs has-subnav"> <section class="snow-view__docs has-subnav">
@ -35,12 +24,12 @@
<Menu /> <Menu />
</aside> </aside>
<Subnav title={title} headings={props.headings} /> <Subnav title={context.title} headers={context.content.headers} />
<article class="snow-view-main"> <article class="snow-view-main">
<div class="content"> <div class="content">
<h2 class="content-title"> <h2 class="content-title">
{title} {context.title}
</h2> </h2>
<div class="content-layout"> <div class="content-layout">
<div class="content-body"> <div class="content-body">
@ -52,4 +41,4 @@
</section> </section>
</div> </div>
</body> </slot:body>

View file

@ -1,20 +1,18 @@
<script hmx="setup"> <script astro>
import Menu from '../components/Menu.hmx'; import Menu from '../components/Menu.hmx';
export function setup() { export const layout = 'layouts/base.hmx';
return { export function setup({ context }) {
layout: 'layouts/base.hmx', return {};
props: {}
};
} }
</script> </script>
<head> <slot:head>
<!-- hi --> <!-- hi -->
<slot></slot> <slot></slot>
</head> </slot:head>
<body class="base 2"> <slot:body>
<div class="container"> <div class="container">
<section class="snow-view__docs is-full"> <section class="snow-view__docs is-full">
@ -28,4 +26,4 @@
</section> </section>
</div> </div>
</body> </slot:body>

View file

@ -1,11 +1,12 @@
<script hmx="setup"> <script astro>
import {format as formatDate, parseISO} from 'date-fns'; import {format as formatDate, parseISO} from 'date-fns';
export function setup() { export const layout = 'layouts/base.hmx';
return { layout: 'layouts/base.hmx', props: {} }; export function setup({ context }) {
return {};
} }
</script> </script>
<head> <slot:head>
<link rel="stylesheet" href="/css/legacy-post.css" /> <link rel="stylesheet" href="/css/legacy-post.css" />
<style> <style>
.markdown-body img, .markdown-body img,
@ -99,10 +100,9 @@
} }
</style> </style>
<slot></slot> <slot></slot>
</head> </slot:head>
<body>
<slot:body>
<div class="grid-extra-space"> <div class="grid-extra-space">
<div class="grid-body-header"> <div class="grid-body-header">
<svg height="80px" style="padding-left: 8px;" viewBox="0 0 640 512" version="1.1" <svg height="80px" style="padding-left: 8px;" viewBox="0 0 640 512" version="1.1"
@ -115,7 +115,7 @@
</g> </g>
</g> </g>
</svg> </svg>
<h1 class="header-snowpack">{title}</h1> <h1 class="header-snowpack">{context.title}</h1>
<p> <p>
@ -140,4 +140,4 @@
<slot></slot> <slot></slot>
</article> </article>
</div> </div>
</body> </slot:body>

View file

@ -1,16 +1,17 @@
<script hmx="setup"> <script astro>
export function setup() { export const layout = 'layouts/main.hmx';
export function setup({ context }) {
return { return {
title: '404 - Not Found', context: {
layout: 'layouts/main.hmx', title: '404 - Not Found',
props: {
} }
}; };
} }
</script> </script>
<h2 class="content-title"> <h2 class="content-title">
{title} {context.title}
</h2> </h2>
<div class="content"> <div class="content">

View file

@ -1,52 +1,56 @@
<script hmx="setup"> <script astro>
import Card from '../components/Card.jsx'; import Card from '../components/Card.jsx';
export const layout = 'layouts/main.hmx';
// mocked for now, to be added later // mocked for now, to be added later
// 1. import {paginate} from 'magicthing'; // 1. import {paginate} from 'magicthing';
// 2. export default function ({paginate}) { // 2. export default function ({paginate}) {
function paginate(options) { function paginate(options) {
if (options.tag === 'guide') { if (options.tag === 'guide') {
return [ return [
{title: 'Test guide 1', href:"#"}, { title: 'Test guide 1', href: "#" },
{title: 'Test guide 2', href:"#"}, { title: 'Test guide 2', href: "#" },
]; ];
} }
if (options.tag === 'communityGuides') { if (options.tag === 'communityGuides') {
return [{title: 'Test communityGuides', href:"#"}]; return [{ title: 'Test communityGuides', href: "#" }];
} }
return []; return [];
} }
export function setup({/* paginate */}) {
export function setup({ context, /* paginate */ }) {
return { return {
title: 'Guides', context: {
description: "Snowpack's usage and integration guides.", title: 'Guides',
layout: 'layouts/main.hmx', description: "Snowpack's usage and integration guides.",
props: { props: {
guides: paginate({ guides: paginate({
files: '/posts/guides/*.md', files: '/posts/guides/*.md',
// sort: ((a, b) => new Date(b) - new Date(a)), // sort: ((a, b) => new Date(b) - new Date(a)),
tag: 'guide', tag: 'guide',
limit: 10, limit: 10,
// page: query.page, // page: query.page,
}), }),
communityGuides: paginate({ communityGuides: paginate({
files: '/posts/guides/*.md', files: '/posts/guides/*.md',
// sort: ((a, b) => new Date(b) - new Date(a)), // sort: ((a, b) => new Date(b) - new Date(a)),
tag: 'communityGuides', tag: 'communityGuides',
limit: 10, limit: 10,
}), }),
}
} }
}; };
} }
</script> </script>
<head> <slot:head>
</head> </slot:head>
<body> <slot:body>
<h2 class="content-title"> <h2 class="content-title">
{title} {context.title}
</h2> </h2>
<h3 class="content-title"> <h3 class="content-title">
@ -56,13 +60,13 @@
<div class="content"> <div class="content">
<ul> <ul>
{props.guides.map((post) => { {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> </ul>
</div> </div>
<br/> <br />
<br/> <br />
<h3 class="content-title"> <h3 class="content-title">
Popular Integration Guides Popular Integration Guides
@ -70,7 +74,8 @@
<div class="card-grid card-grid-4"> <div class="card-grid card-grid-4">
{props.communityGuides.map((post) => { {props.communityGuides.map((post) => {
return <Card item={post} />; return
<Card item={post} />;
})} })}
</div> </div>
</body> </slot:body>

View file

@ -1,22 +1,21 @@
<script hmx="setup"> <script astro>
import Menu from '../components/Menu.hmx'; import Menu from '../components/Menu.hmx';
import Hero from '../components/Hero.hmx'; import Hero from '../components/Hero.hmx';
export function setup() {
return { export const layout = 'layouts/base.hmx';
layout: 'layouts/base.hmx', export function setup({context}) {
props: {} return {}
}
} }
</script> </script>
<head> <slot:head>
<!-- Head Stuff --> <meta charset="AAA" />
</head> </slot:head>
<body> <slot:body>
<Hero bar={title}></Hero> <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"> <section class="snow-view__docs is-full is-home">
<aside id="nav-primary" class="snow-view-nav"> <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. --> <!-- 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> <script async="async" defer="defer" src="https://buttons.github.io/buttons.js"></script>
</body> </slot:body>

View file

@ -1,31 +1,33 @@
<script hmx="setup"> <script astro>
import Card from '../components/Card.jsx'; import Card from '../components/Card.jsx';
import CompanyLogo from '../components/CompanyLogo.jsx'; import CompanyLogo from '../components/CompanyLogo.jsx';
import NewsAssets from '../components/NewsAssets.svelte'; import NewsAssets from '../components/NewsAssets.svelte';
import NewsTitle from '../components/NewsTitle.vue'; import NewsTitle from '../components/NewsTitle.vue';
export const layout = 'layouts/main.hmx';
import news from '../data/news.json'; import news from '../data/news.json';
import users from '../data/users.json'; import users from '../data/users.json';
export function setup({ findContent }) { export function setup({ context, request }) {
console.log(request);
return { return {
title: 'Community & News', context: {
description: "Snowpack community news and companies that use Snowpack.", title: 'Community & News',
layout: 'layouts/main.hmx', description: "Snowpack community news and companies that use Snowpack.",
// Using Snowpack? Want to be featured on snowpack.dev? // Using Snowpack? Want to be featured on snowpack.dev?
// Add your project, organization, or company to the end of this list! // Add your project, organization, or company to the end of this list!
props: {
news, news,
users, users,
} }
}; }
} }
</script> </script>
<head> </head> <slot:head> </slot:head>
<body> <slot:body>
<NewsTitle title={title} /> <NewsTitle title={context.title} />
<p> <p>
Get the latest news, blog posts, and tutorials on Snowpack. <a href="/feed.xml">Also available via RSS.</a> 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> working on!</div>
</article> </article>
{props.news.reverse().map((item) => <Card:dynamic item={item} />)} {context.news.reverse().map((item) =>
<Card:dynamic item={item} />)}
</div> </div>
<div class="content"> <div class="content">
@ -54,7 +57,8 @@
<div class="company-logos"> <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" <a href="https://github.com/snowpackjs/snowpack/edit/main/www/_template/news.md" target="_blank"
title="Add Your Project/Company!" class="add-company-button"> title="Add Your Project/Company!" class="add-company-button">
@ -70,4 +74,4 @@
<NewsAssets /> <NewsAssets />
</div> </div>
</body> </slot:body>

View file

@ -1,29 +1,24 @@
<script hmx="setup"> <script astro>
import news from '../data/news.json';
import users from '../data/users.json';
import PluginSearchPage from '../components/PluginSearchPage.jsx'; import PluginSearchPage from '../components/PluginSearchPage.jsx';
export function setup({ findContent }) { export const layout = 'layouts/main.hmx';
export function setup({ context }) {
return { return {
title: 'The Snowpack Plugin Catalog', context: {
description: 'Snowpack plugins allow for configuration-minimal tooling integration.', title: 'The Snowpack Plugin Catalog',
layout: 'layouts/main.hmx', description: 'Snowpack plugins allow for configuration-minimal tooling integration.',
// Using Snowpack? Want to be featured on snowpack.dev?
// Add your project, organization, or company to the end of this list!
props: {
news,
users,
} }
}; };
} }
</script> </script>
<head> <slot:head>
</head> </slot:head>
<body> <slot:body>
<h2 class="content-title"> <h2 class="content-title">
{ title } { context.title }
</h2> </h2>
<h3 class="pluginPage-subheading">Customize Snowpack with optimized build plugins.</h3> <h3 class="pluginPage-subheading">Customize Snowpack with optimized build plugins.</h3>
@ -33,4 +28,4 @@
</p> </p>
<PluginSearchPage:dynamic /> <PluginSearchPage:dynamic />
</body> </slot:body>

View file

@ -1,4 +1,4 @@
<script hmx="setup"> <script astro>
import Subnav from '../components/Subnav.hmx'; import Subnav from '../components/Subnav.hmx';
import { content as Menu } from '../components/Menu.hmx'; import { content as Menu } from '../components/Menu.hmx';
// import contentful from 'skypack:contentful'; // import contentful from 'skypack:contentful';
@ -16,12 +16,12 @@
<Menu /> <Menu />
</aside> </aside>
<Subnav title={title} /> <Subnav title={context.title} headers={context.content.headers} />
<article class="snow-view-main"> <article class="snow-view-main">
<div class="content"> <div class="content">
<h2 class="content-title"> <h2 class="content-title">
{title} {context.title}
</h2> </h2>
<div class="content-layout"> <div class="content-layout">
<div class="content-body"> <div class="content-body">

21
package-lock.json generated
View file

@ -167,6 +167,12 @@
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz",
"integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==" "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": { "@types/json-schema": {
"version": "7.0.7", "version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
@ -1562,6 +1568,21 @@
"pump": "^3.0.0" "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": { "glob": {
"version": "7.1.6", "version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",

View file

@ -36,6 +36,7 @@
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"domhandler": "^4.0.0", "domhandler": "^4.0.0",
"es-module-lexer": "^0.4.1", "es-module-lexer": "^0.4.1",
"github-slugger": "^1.3.0",
"gray-matter": "^4.0.2", "gray-matter": "^4.0.2",
"htmlparser2": "^6.0.0", "htmlparser2": "^6.0.0",
"kleur": "^4.1.4", "kleur": "^4.1.4",
@ -54,6 +55,7 @@
"yargs-parser": "^20.2.7" "yargs-parser": "^20.2.7"
}, },
"devDependencies": { "devDependencies": {
"@types/github-slugger": "^1.3.0",
"@types/sass": "^1.16.0", "@types/sass": "^1.16.0",
"@types/yargs-parser": "^20.2.0", "@types/yargs-parser": "^20.2.0",
"@typescript-eslint/eslint-plugin": "^4.18.0", "@typescript-eslint/eslint-plugin": "^4.18.0",

View file

@ -12,58 +12,10 @@ module.exports = function (snowpackConfig, { resolve } = {}) {
output: ['.js'], output: ['.js'],
}, },
async load({ filePath }) { async load({ filePath }) {
const { compilePage, compileComponent } = await transformPromise; const { compileComponent } = await transformPromise;
const projectRoot = snowpackConfig.root; const projectRoot = snowpackConfig.root;
const contents = await readFile(filePath, 'utf-8'); const contents = await readFile(filePath, 'utf-8');
if (!filePath.includes('/pages/') && !filePath.includes('/layouts/')) {
const result = await compileComponent(contents, { compileOptions: { resolve }, filename: filePath, projectRoot }); 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; return result.contents;
}, },
}; };

View file

@ -17,9 +17,12 @@ export interface JsxItem {
export interface TransformResult { export interface TransformResult {
script: string; script: string;
head: JsxItem | undefined;
body: JsxItem | undefined;
items: JsxItem[]; items: JsxItem[];
} }
export interface CompileResult { export interface CompileResult {
result: TransformResult;
contents: string; contents: string;
} }

View file

@ -53,6 +53,10 @@ function getAttributes(attrs: Attribute[]): Record<string, string> {
continue; continue;
} }
const val: TemplateNode = attr.value[0]; const val: TemplateNode = attr.value[0];
if (!val) {
result[attr.name] = '(' + val + ')';
continue;
}
switch (val.type) { switch (val.type) {
case 'MustacheTag': case 'MustacheTag':
result[attr.name] = '(' + val.expression + ')'; 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 { 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. // esbuild treeshakes unused imports. In our case these are components, so let's keep them.
const imports = eslexer const imports = eslexer
.parse(raw)[0] .parse(raw)[0]
.filter(({ d }) => d === -1) .filter(({ d }) => d === -1)
.map((i: any) => raw.substring(i.ss, i.se)); .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, { let { code } = transformSync(raw, {
loader, loader,
jsxFactory: 'h', jsxFactory: 'h',
jsxFragment: 'Fragment', jsxFragment: 'Fragment',
charset: 'utf8', charset: 'utf8',
}); });
for (let importStatement of imports) {
if (!code.includes(importStatement)) {
code = importStatement + '\n' + code;
}
}
return code; return code;
} }
export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Promise<TransformResult> { export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Promise<TransformResult> {
await eslexer.init; await eslexer.init;
// Compile scripts as TypeScript, always // 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 // Todo: Validate that `h` and `Fragment` aren't defined in the script
const [scriptImports] = eslexer.parse(script, 'optional-sourcename'); 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>(); const additionalImports = new Set<string>();
let headItem: JsxItem | undefined;
let bodyItem: JsxItem | undefined;
let items: JsxItem[] = []; let items: JsxItem[] = [];
let mode: 'JSX' | 'SCRIPT' | 'SLOT' = 'JSX'; let mode: 'JSX' | 'SCRIPT' | 'SLOT' = 'JSX';
let collectionItem: JsxItem | undefined; let collectionItem: JsxItem | undefined;
@ -192,7 +202,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
enter(node: TemplateNode) { enter(node: TemplateNode) {
switch (node.type) { switch (node.type) {
case 'MustacheTag': case 'MustacheTag':
let code = compileScriptSafe(node.expression, 'jsx'); let code = compileExpressionSafe(node.expression, 'jsx');
let matches: RegExpExecArray[] = []; let matches: RegExpExecArray[] = [];
let match: RegExpExecArray | null | undefined; let match: RegExpExecArray | null | undefined;
@ -230,8 +240,12 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
return; return;
} }
break; break;
case 'Head':
case 'Body':
case 'InlineComponent': case 'InlineComponent':
case 'Element': case 'Title':
case 'Element': {
const name: string = node.name; const name: string = node.name;
if (!name) { if (!name) {
throw new Error('AHHHH'); throw new Error('AHHHH');
@ -241,6 +255,16 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
currentItemName = name; currentItemName = name;
if (!collectionItem) { if (!collectionItem) {
collectionItem = { name, jsx: '' }; 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); items.push(collectionItem);
} }
collectionItem.jsx += collectionItem.jsx === '' ? '' : ','; 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'}`; collectionItem.jsx += `h("${name}", ${attributes ? generateAttributes(attributes) : 'null'}`;
return; return;
} }
if (name === 'Component') {
collectionItem.jsx += `h(Fragment, null`;
return;
}
const [componentName, componentKind] = name.split(':'); const [componentName, componentKind] = name.split(':');
const componentImportData = components[componentName]; const componentImportData = components[componentName];
if (!componentImportData) { if (!componentImportData) {
@ -265,6 +285,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
collectionItem.jsx += `h(${wrapper}, ${attributes ? generateAttributes(attributes) : 'null'}`; collectionItem.jsx += `h(${wrapper}, ${attributes ? generateAttributes(attributes) : 'null'}`;
return; return;
}
case 'Attribute': { case 'Attribute': {
this.skip(); this.skip();
return; return;
@ -293,7 +314,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
return; return;
} }
default: default:
throw new Error('Unexpected node type: ' + node.type); throw new Error('Unexpected (enter) node type: ' + node.type);
} }
}, },
leave(node, parent, prop, index) { leave(node, parent, prop, index) {
@ -314,6 +335,9 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
if (!collectionItem) { if (!collectionItem) {
return; return;
} }
case 'Head':
case 'Body':
case 'Title':
case 'Element': case 'Element':
case 'InlineComponent': case 'InlineComponent':
if (!collectionItem) { if (!collectionItem) {
@ -329,13 +353,15 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
return; return;
} }
default: default:
throw new Error('Unexpected node type: ' + node.type); throw new Error('Unexpected (leave) node type: ' + node.type);
} }
}, },
}); });
return { return {
script: script + '\n' + Array.from(additionalImports).join('\n'), script: script + '\n' + Array.from(additionalImports).join('\n'),
head: headItem,
body: bodyItem,
items, items,
}; };
} }

View file

@ -58,7 +58,7 @@ export interface Parser {
export interface Script extends BaseNode { export interface Script extends BaseNode {
type: 'Script'; type: 'Script';
context: string; context: 'runtime' | 'setup';
content: string; content: string;
} }
@ -75,8 +75,8 @@ export interface Style extends BaseNode {
export interface Ast { export interface Ast {
html: TemplateNode; html: TemplateNode;
css: Style; css: Style;
instance: Script;
module: Script; module: Script;
// instance: Script;
} }
export interface Warning { 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 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 { export interface Visitor {
enter: (node: Node) => void; enter: (node: Node) => void;
leave?: (node: Node) => void; leave?: (node: Node) => void;

View file

@ -232,33 +232,34 @@ export default function parse(template: string, options: ParserOptions = {}): As
); );
} }
const instance_scripts = parser.js.filter((script) => script.context === 'default'); // const instance_scripts = parser.js.filter((script) => script.context === 'default');
const module_scripts = parser.js.filter((script) => script.context === 'module'); // 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( parser.error(
{ {
code: 'invalid-script', 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) { // if (module_scripts.length > 1) {
parser.error( // parser.error(
{ // {
code: 'invalid-script', // code: 'invalid-script',
message: 'A component can only have one <script context="module"> element', // message: 'A component can only have one <script context="module"> element',
}, // },
module_scripts[1].start // module_scripts[1].start
); // );
} // }
return { return {
html: parser.html, html: parser.html,
css: parser.css[0], css: parser.css[0],
instance: instance_scripts[0], // instance: instance_scripts[0],
module: module_scripts[0], module: hmx_scripts[0],
}; };
} }

View file

@ -7,15 +7,16 @@ import { Node, Program } from 'estree';
const script_closing_tag = '</script>'; const script_closing_tag = '</script>';
function get_context(parser: Parser, attributes: any[], start: number): string { function get_context(parser: Parser, attributes: any[], start: number): 'runtime' | 'setup' {
const context = attributes.find((attribute) => attribute.name === 'context'); const context = attributes.find((attribute) => attribute.name === 'astro');
if (!context) return 'default'; if (!context) return 'runtime';
if (context.value === true) return 'setup';
if (context.value.length !== 1 || context.value[0].type !== 'Text') { if (context.value.length !== 1 || context.value[0].type !== 'Text') {
parser.error( parser.error(
{ {
code: 'invalid-script', code: 'invalid-script',
message: 'context attribute must be static', message: 'astro attribute must be static',
}, },
start start
); );
@ -23,11 +24,11 @@ function get_context(parser: Parser, attributes: any[], start: number): string {
const value = context.value[0].data; const value = context.value[0].data;
if (value !== 'module') { if (value !== 'setup') {
parser.error( parser.error(
{ {
code: 'invalid-script', 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 context.start
); );

View file

@ -14,13 +14,14 @@ import list from '../../utils/list.js';
const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const meta_tags = new Map([ const meta_tags = new Map([
['svelte:head', 'Head'], ['slot:head', 'Head'],
['svelte:options', 'Options'], ['slot:body', 'Body'],
['svelte:window', 'Window'], // ['astro:options', 'Options'],
['svelte:body', 'Body'], // ['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([ const specials = new Map([
[ [
@ -39,9 +40,10 @@ const specials = new Map([
], ],
]); ]);
const SELF = /^svelte:self(?=[\s/>])/; const SELF = /^astro:self(?=[\s/>])/;
const COMPONENT = /^svelte:component(?=[\s/>])/; const COMPONENT = /^astro:component(?=[\s/>])/;
const SLOT = /^svelte:fragment(?=[\s/>])/; const SLOT = /^astro:fragment(?=[\s/>])/;
const HEAD = /^head(?=[\s/>])/;
function parent_is_head(stack) { function parent_is_head(stack) {
let i = stack.length; let i = stack.length;
@ -79,7 +81,7 @@ export default function tag(parser: Parser) {
if (meta_tags.has(name)) { if (meta_tags.has(name)) {
const slug = meta_tags.get(name).toLowerCase(); const slug = meta_tags.get(name).toLowerCase();
if (is_closing_tag) { 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( parser.error(
{ {
code: `invalid-${slug}-content`, code: `invalid-${slug}-content`,
@ -115,9 +117,9 @@ export default function tag(parser: Parser) {
const type = meta_tags.has(name) const type = meta_tags.has(name)
? meta_tags.get(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' ? 'InlineComponent'
: name === 'svelte:fragment' : name === 'astro:fragment'
? 'SlotTemplate' ? 'SlotTemplate'
: name === 'title' && parent_is_head(parser.stack) : name === 'title' && parent_is_head(parser.stack)
? 'Title' ? 'Title'
@ -197,13 +199,13 @@ export default function tag(parser: Parser) {
parser.allow_whitespace(); parser.allow_whitespace();
} }
if (name === 'svelte:component') { if (name === 'astro:component') {
const index = element.attributes.findIndex((attr) => attr.type === 'Attribute' && attr.name === 'this'); const index = element.attributes.findIndex((attr) => attr.type === 'Attribute' && attr.name === 'this');
if (!~index) { if (!~index) {
parser.error( parser.error(
{ {
code: 'missing-component-definition', code: 'missing-component-definition',
message: "<svelte:component> must have a 'this' attribute", message: "<astro:component> must have a 'this' attribute",
}, },
start start
); );
@ -281,27 +283,29 @@ function read_tag_name(parser: Parser) {
parser.error( parser.error(
{ {
code: 'invalid-self-placement', 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 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|\/|>)/); const name = parser.read_until(/(\s|\/|>)/);
if (meta_tags.has(name)) return name; if (meta_tags.has(name)) return name;
if (name.startsWith('svelte:')) { if (name.startsWith('astro:')) {
const match = fuzzymatch(name.slice(7), valid_meta_tags); 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}'?)`; if (match) message += ` (did you mean '${match}'?)`;
parser.error( parser.error(

View file

@ -52,6 +52,7 @@ export default async function (astroConfig: AstroConfig) {
await writeFile(outPath, html, 'utf-8'); await writeFile(outPath, html, 'utf-8');
} catch (err) { } catch (err) {
console.error('Unable to generate page', rel); console.error('Unable to generate page', rel);
console.error(err);
} }
} }

View file

@ -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 };

View file

@ -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,
};
}

View file

@ -58,7 +58,14 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
try { try {
const mod = await snowpackRuntime.importModule(selectedPageUrl); 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 { return {
statusCode: 200, statusCode: 200,

View file

@ -7,7 +7,7 @@ import matter from 'gray-matter';
import gfmHtml from 'micromark-extension-gfm/html.js'; import gfmHtml from 'micromark-extension-gfm/html.js';
import { CompileResult, TransformResult } from './@types/astro'; import { CompileResult, TransformResult } from './@types/astro';
import { parse } from './compiler/index.js'; 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 { defaultLogOptions } from './logger.js';
import { optimize } from './optimize/index.js'; import { optimize } from './optimize/index.js';
import { codegen } from './codegen/index.js'; import { codegen } from './codegen/index.js';
@ -51,33 +51,35 @@ async function convertMdToJsx(
contents: string, contents: string,
{ compileOptions, filename, fileID }: { compileOptions: CompileOptions; filename: string; fileID: string } { compileOptions, filename, fileID }: { compileOptions: CompileOptions; filename: string; fileID: string }
): Promise<TransformResult> { ): Promise<TransformResult> {
// This doesn't work.
const { data: _frontmatterData, content } = matter(contents); const { data: _frontmatterData, content } = matter(contents);
const {headers, headersExtension} = createMarkdownHeadersCollector();
const mdHtml = micromark(content, { const mdHtml = micromark(content, {
extensions: [gfmSyntax()], extensions: [gfmSyntax()],
htmlExtensions: [gfmHtml, markdownEncode], htmlExtensions: [gfmHtml, headersExtension],
}); });
const setupData = { console.log("headers", headers);
title: _frontmatterData.title, const setupContext = {
description: _frontmatterData.description, ..._frontmatterData,
layout: _frontmatterData.layout,
content: { content: {
frontmatter: _frontmatterData, frontmatter: _frontmatterData,
headers,
// This is an awful hack due to Svelte parser disliking script tags badly. source: content,
source: content.replace(/<\/?script/g, '<SCRIPT'),
html: mdHtml, 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( return convertHmxToJsx(
`<script hmx="setup">export function setup() { `<script astro>
return ${JSON.stringify(setupData)}; ${_frontmatterData.layout ? `export const layout = ${JSON.stringify(_frontmatterData.layout)};` : ''}
}</script><head></head><body>${mdHtml}</body>`, export function setup({context}) {
return {context: ${stringifiedSetupContext} };
}
</script><slot:head></slot:head><slot:body><section>{${JSON.stringify(mdHtml)}}</section></slot:body>`,
{ compileOptions, filename, fileID } { compileOptions, filename, fileID }
); );
} }
@ -97,49 +99,85 @@ async function transformFromSource(
} }
} }
export async function compilePage(
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.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');
const modJsx = `
${sourceJsx.script}
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 async function compileComponent( export async function compileComponent(
source: string, source: string,
{ compileOptions = defaultCompileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string } { compileOptions = defaultCompileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string }
): Promise<CompileResult> { ): Promise<CompileResult> {
const sourceJsx = await transformFromSource(source, { compileOptions, filename, projectRoot }); const sourceJsx = await transformFromSource(source, { compileOptions, filename, projectRoot });
const headItem = sourceJsx.head;
// throw error if <Component /> missing const bodyItem = sourceJsx.body;
if (!sourceJsx.items.find(({ name }) => name === 'Component')) throw new Error(`${filename} <Component> expected!`); const headItemJsx = !headItem ? 'null' : headItem.jsx;
const bodyItemJsx = !bodyItem ? 'null' : bodyItem.jsx;
// sort <style> tags first // sort <style> tags first
// TODO: remove these and inject in <head> // TODO: remove these and inject in <head>
sourceJsx.items.sort((a, b) => (a.name === 'style' && b.name !== 'style' ? -1 : 0)); sourceJsx.items.sort((a, b) => (a.name === 'style' && b.name !== 'style' ? -1 : 0));
// return template // return template
const modJsx = ` let modJsx = `
import { h, Fragment } from '${internalImport('h.js')}'; // <script astro></script>
export default function(props) { return h(Fragment, null, ${sourceJsx.items.map(({ jsx }) => jsx).join(',')}); } ${sourceJsx.script}
`.trim();
// \`__render()\`: Render the contents of the HMX module. "<slot:*>" elements are not
// included (see below).
import { h, Fragment } from '${internalImport('h.js')}';
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,
};
// 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 { return {
result: sourceJsx,
contents: modJsx, contents: modJsx,
}; };
} }

View file

@ -1,4 +1,4 @@
<script hmx="setup"> <script astro>
export function setup() { export function setup() {
return { return {
props: {} props: {}
@ -6,10 +6,10 @@
} }
</script> </script>
<head> <slot:head>
<!-- Head Stuff --> <!-- Head Stuff -->
</head> </slot:head>
<body> <slot:body>
<h1>Hello world!</h1> <h1>Hello world!</h1>
</body> </slot:body>