Redesign pages, remove layout nesting (#24)

* wip

* new svelte-style prop declaration is working

* got it working!

* revert h changes

* format

* style lang update
This commit is contained in:
Fred K. Schott 2021-03-24 16:01:28 -07:00 committed by GitHub
parent 3c24faa8ca
commit a72ab10c62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 751 additions and 714 deletions

View file

@ -0,0 +1,38 @@
---
import Banner from './Banner.astro';
import Nav from './Nav.astro';
export let title: string;
export let description: string;
export let permalink: string;
---
<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="manifest" href="/favicon/site.webmanifest" />
<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content="{description}" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content={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" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={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" />
<!-- 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" />

View file

@ -0,0 +1,20 @@
---
import Banner from './Banner.astro';
import Nav from './Nav.astro';
---
<Banner></Banner>
<Nav />
<slot></slot>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-130280175-9"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-130280175-9', { page_path: location.pathname === '/' ? (location.pathname + location.hash) : (location.pathname) });
</script>

View file

@ -0,0 +1,23 @@
---
import BaseLayout from './BaseLayout.astro';
import Menu from './Menu.astro';
export function setup({ context }) {
return {};
}
---
<BaseLayout>
<div class="container">
<section class="snow-view__docs is-full">
<aside id="nav-primary" class="snow-view-nav">
<Menu />
</aside>
<article class="snow-view-main">
<slot></slot>
</article>
</section>
</div>
</BaseLayout>

View file

@ -1,5 +1,5 @@
--- ---
export let props: { version: string }; export let version: string = '3.1.2';
--- ---
<style lang="scss"> <style lang="scss">
@ -257,7 +257,7 @@
</div> </div>
<div style="flex-grow: 1"></div> <div style="flex-grow: 1"></div>
<a href="https://github.com/snowpackjs/snowpack/releases" target="_blank" class="link version"> <a href="https://github.com/snowpackjs/snowpack/releases" target="_blank" class="link version">
{`v${props.version}`} {`v${version}`}
</a> </a>
<a href="https://github.com/snowpackjs/snowpack" target="_blank" class="link link__desktop"> <a href="https://github.com/snowpackjs/snowpack" target="_blank" class="link link__desktop">
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="snow-icon" role="img" <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="snow-icon" role="img"

View file

@ -1,9 +1,7 @@
--- ---
export let props: { export let title: string;
title: string, export let inputPath: string;
inputPath: string, export let headers: string;
headers: { text: string, slug: string }[]
};
--- ---
<style lang="scss"> <style lang="scss">
@ -59,12 +57,12 @@
<script type="module" defer src="/js/index.js"></script> <script type="module" defer src="/js/index.js"></script>
<aside class="subnav"> <aside class="subnav">
{props.headers.length > 0 && ( {headers.length > 0 && (
<div> <div>
<h4 class="header">On this page</h4> <h4 class="header">On this page</h4>
<nav class="toc"> <nav class="toc">
<ol> <ol>
{props.headers.map((heading) => { {headers.map((heading) => {
return <li><a href={"#" + heading.slug}>{heading.text}</a></li> return <li><a href={"#" + heading.slug}>{heading.text}</a></li>
})} })}
</ol> </ol>
@ -74,5 +72,5 @@
)} )}
<h4 class="header">Suggest a change</h4> <h4 class="header">Suggest a change</h4>
<a href="https://github.com/snowpackjs/snowpack/blob/main/www/{props.inputPath}">Edit this page on GitHub</a> <a href="https://github.com/snowpackjs/snowpack/blob/main/www/{inputPath}">Edit this page on GitHub</a>
</aside> </aside>

View file

@ -1,62 +0,0 @@
---
import Banner from '../components/Banner.astro';
import Nav from '../components/Nav.astro';
export function setup({ context }) {
return {
context: {
title: 'Snowpack',
description: 'Snowpack is a lightning-fast frontend build tool, designed for the modern web.',
currentSnowpackVersion: '3.0.13',
}
};
}
---
<astro: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="manifest" href="/favicon/site.webmanifest" />
<!-- Primary Meta Tags -->
<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={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={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" />
</astro:head>
<Banner></Banner>
<Nav version={context.currentSnowpackVersion} />
<!-- if no slot given, assume add to bottom -->
<slot></slot>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-130280175-9"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-130280175-9', { page_path: location.pathname === '/' ? (location.pathname + location.hash) : (location.pathname) });
</script>

View file

@ -1,78 +1,88 @@
--- ---
import Menu from '../components/Menu.astro'; import Subnav from '../components/Subnav.astro';
import Subnav from '../components/Subnav.astro'; import Menu from '../components/Menu.astro';
import BaseHead from '../components/BaseHead.astro';
import BaseLayout from '../components/BaseLayout.astro';
export const layout = 'layouts/base.astro'; export let content: any;
export function setup({ context }) {
return {};
}
--- ---
<html>
<style> <head>
.cover-wrapper { <style>
width: 100%; .cover-wrapper {
height: 44vh; width: 100%;
min-height: 20rem; height: 44vh;
max-height: 30rem; min-height: 20rem;
position: relative; max-height: 30rem;
background: #2a85ca40; position: relative;
overflow: hidden; background: #2a85ca40;
} overflow: hidden;
.cover, }
.cover-blur { .cover,
position: absolute; .cover-blur {
top: 0; position: absolute;
left: 0; top: 0;
bottom: 0; left: 0;
right: 0; bottom: 0;
height: 100%; right: 0;
width: 100%; height: 100%;
} width: 100%;
.cover-blur { }
object-fit: cover;
filter: blur(3px) brightness(1.5);
transform: scale(1.1);
}
.cover {
object-fit: contain;
filter: brightness(1.5);
}
@media (max-width: 1200px) {
.cover-blur { .cover-blur {
object-fit: cover; object-fit: cover;
filter: blur(3px) brightness(1.5);
transform: scale(1.1);
} }
.cover { .cover {
object-fit: cover; object-fit: contain;
filter: brightness(1.5);
} }
} @media (max-width: 1200px) {
</style> .cover-blur {
object-fit: cover;
}
.cover {
object-fit: cover;
}
}
</style>
<BaseHead title={content.title} description={content.description} permalink="TODO" />
</head>
<div class="cover-wrapper"> <body>
<img class="cover-blur" src={context.cover} alt=""/> <BaseLayout>
<img class="cover" src={context.cover} alt=""/>
</div>
<div class="container"> <div class="cover-wrapper">
<section class="snow-view__docs has-subnav"> <img class="cover-blur" src={content.cover} alt=""/>
<img class="cover" src={content.cover} alt=""/>
</div>
<aside id="nav-primary" class="snow-view-nav"> <div class="container">
<Menu /> <section class="snow-view__docs has-subnav">
</aside>
<article class="snow-view-main"> <aside id="nav-primary" class="snow-view-nav">
<div class="content"> <Menu />
<h2 class="content-title"> </aside>
{context.title}
</h2> <article class="snow-view-main">
<div class="content-layout"> <div class="content">
<div class="content-body"> <h2 class="content-title">
<slot></slot> {content.title}
</h2>
<div class="content-layout">
<div class="content-body">
{content.body}
</div>
</div>
</div> </div>
</div> </article>
</div>
</article>
<Subnav title={context.title} headers={context.content.headers} /> <Subnav title={content.title} headers={content.headers} />
</section> </section>
</div> </div>
</BaseLayout>
</body>
</html>

View file

@ -1,37 +1,45 @@
--- ---
import Subnav from '../components/Subnav.astro'; import Subnav from '../components/Subnav.astro';
import Menu from '../components/Menu.astro'; import Menu from '../components/Menu.astro';
import BaseHead from '../components/BaseHead.astro';
import BaseLayout from '../components/BaseLayout.astro';
export const layout = 'layouts/base.astro'; export let content: any;
export function setup({ context }) {
return {
context: {
}
};
}
--- ---
<div class="container"> <html>
<section class="snow-view__docs has-subnav">
<aside id="nav-primary" class="snow-view-nav"> <head>
<Menu /> <BaseHead title={content.title} description={content.description} permalink="TODO" />
</aside> </head>
<Subnav title={context.title} headers={context.content.headers} /> <body>
<BaseLayout>
<div class="container">
<section class="snow-view__docs has-subnav">
<article class="snow-view-main"> <aside id="nav-primary" class="snow-view-nav">
<div class="content"> <Menu />
<h2 class="content-title"> </aside>
{context.title}
</h2> <Subnav title={content.title} headers={content.headers} />
<div class="content-layout">
<div class="content-body"> <article class="snow-view-main">
<slot></slot> <div class="content">
<h2 class="content-title">
{content.title}
</h2>
<div class="content-layout">
<div class="content-body">
<slot></slot>
</div>
</div>
</div> </div>
</div> </article>
</div>
</article>
</section> </section>
</div> </div>
</BaseLayout>
</body>
</html>

View file

@ -1,21 +0,0 @@
---
import Menu from '../components/Menu.astro';
export const layout = 'layouts/base.astro';
export function setup({ context }) {
return {};
}
---
<div class="container">
<section class="snow-view__docs is-full">
<aside id="nav-primary" class="snow-view-nav">
<Menu />
</aside>
<article class="snow-view-main">
<slot></slot>
</article>
</section>
</div>

View file

@ -1,127 +1,136 @@
--- ---
import { format as formatDate, parseISO } from 'date-fns'; import BaseHead from '../components/BaseHead.astro';
export const layout = 'layouts/base.astro'; import BaseLayout from '../components/BaseLayout.astro';
export function setup({ context }) { import { format as formatDate, parseISO } from 'date-fns';
return {};
} export let content: any;
--- ---
<astro:head> <html>
<link rel="stylesheet" href="/css/legacy-post.css" />
</astro:head>
<style>
.markdown-body img,
.markdown-body video,
.markdown-body iframe {
box-shadow: 0px 5px 12px 0 #CCC;
border-radius: 3px;
min-width: 130%;
width: 130%;
margin-left: -15%;
margin-right: -15%;
margin-top: 4rem;
margin-bottom: -1rem;
}
@media (max-width: 860px) {
<head>
<style>
.markdown-body img, .markdown-body img,
.markdown-body video, .markdown-body video,
.markdown-body iframe { .markdown-body iframe {
min-width: 100%; box-shadow: 0px 5px 12px 0 #CCC;
margin-left: 0; border-radius: 3px;
margin-right: 0; min-width: 130%;
margin-bottom: -2rem; width: 130%;
margin-left: -15%;
margin-right: -15%;
margin-top: 4rem;
margin-bottom: -1rem;
} }
}
@media (max-width: 860px) {
.markdown-body table td:nth-child(1) {
white-space: nowrap; .markdown-body img,
} .markdown-body video,
.markdown-body iframe {
.markdown-body, min-width: 100%;
.fbody-header { margin-left: 0;
max-width: 840px; margin-right: 0;
} margin-bottom: -2rem;
}
.markdown-body { }
font-size: 18px;
margin-bottom: 20vh; .markdown-body table td:nth-child(1) {
} white-space: nowrap;
}
.markdown-body h2 {
font-size: 1.8em;
}
@media (max-width: 860px) {
.markdown-body, .markdown-body,
.toc { .fbody-header {
padding: 1em; max-width: 840px;
} }
}
.markdown-body {
@media (max-width: 740px) { font-size: 18px;
.markdown-body img, margin-bottom: 20vh;
.markdown-body iframe {
max-width: 108%;
margin-left: -4%;
margin-right: -4%;
} }
.grid-body { .markdown-body h2 {
padding: 20px 0 0 0; font-size: 1.8em;
} }
}
@media (max-width: 860px) {
.markdown-body,
.toc {
padding: 1em;
}
}
@media (max-width: 740px) {
.markdown-body img,
.markdown-body iframe {
max-width: 108%;
margin-left: -4%;
margin-right: -4%;
}
.grid-body {
padding: 20px 0 0 0;
}
}
.header-snowpack {
font-size: 3.5rem;
text-align: center;
}
.markdown-body h1 {
font-size: 3.5em;
}
.markdown-body h3 .header-link {
opacity: 1;
}
</style>
<BaseHead title={content.title} description={content.description} permalink="TODO" />
<link rel="stylesheet" href="/css/legacy-post.css" />
</head>
.header-snowpack { <body>
font-size: 3.5rem; <BaseLayout>
text-align: center;
}
.markdown-body h1 { <div class="grid-extra-space">
font-size: 3.5em; <div class="grid-body-header">
} <svg height="80px" style="padding-left: 8px;" viewBox="0 0 640 512" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
.markdown-body h3 .header-link { <g id="Page-1" stroke="none" stroke-width="1" fill="currentColor" fill-rule="evenodd">
opacity: 1; <g id="mountain-solid" transform="translate(-1.000000, 0.000000)" fill-rule="nonzero">
} <path
</style> 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>
<div class="grid-extra-space"> </g>
<div class="grid-body-header"> </g>
<svg height="80px" style="padding-left: 8px;" viewBox="0 0 640 512" version="1.1" xmlns="http://www.w3.org/2000/svg" </svg>
xmlns:xlink="http://www.w3.org/1999/xlink"> <h1 class="header-snowpack">{content.title}</h1>
<g id="Page-1" stroke="none" stroke-width="1" fill="currentColor" fill-rule="evenodd">
<g id="mountain-solid" 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>
</g>
</g>
</svg>
<h1 class="header-snowpack">{context.title}</h1>
<div> <div>
{context.tagline && <div style="margin-bottom: 1rem;">{context.tagline}</div>} {content.tagline && <div style="margin-bottom: 1rem;">{content.tagline}</div>}
<div> <div>
Published <a href='#published-at'>{formatDate(parseISO(context.date), 'MMMM d, yyyy')}</a> Published <a href='#published-at'>{formatDate(parseISO(content.date), 'MMMM d, yyyy')}</a>
by <a href="https://twitter.com/FredKSchott">Fred K. Schott</a> by <a href="https://twitter.com/FredKSchott">Fred K. Schott</a>
</div>
</div>
<!-- Place this tag where you want the button to render. -->
<div class="hidden-mobile" style="text-align: center; margin-top: 0.5rem; filter: scale(2);">
<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>
<!-- Place this tag in your head or just before your close body tag. -->
<script defer src="https://buttons.github.io/buttons.js"></script>
</div> </div>
</div> </div>
<div class="grid-body">
<!-- Place this tag where you want the button to render. --> <article class="markdown-body">
<div class="hidden-mobile" style="text-align: center; margin-top: 0.5rem; filter: scale(2);"> <slot></slot>
<a class="github-button" href="https://github.com/snowpackjs/snowpack" data-icon="octicon-star" data-size="large" </article>
data-show-count="true" aria-label="Star snowpackjs/snowpack on GitHub">Star</a>
</div> </div>
<!-- Place this tag in your head or just before your close body tag. --> </BaseLayout>
<script defer src="https://buttons.github.io/buttons.js"></script> </body>
</div>
</div> </html>
<div class="grid-body">
<article class="markdown-body">
<slot></slot>
</article>
</div>

View file

@ -1,19 +1,26 @@
--- ---
export const layout = 'layouts/main.astro'; import BaseHead from '../components/BaseHead.astro';
import MainLayout from '../components/MainLayout.astro';
export function setup({ context }) { let title = 'Not Found';
return { let description = 'Snowpack is a lightning-fast frontend build tool, designed for the modern web.';
context: {
title: '404 - Not Found',
}
};
}
--- ---
<h2 class="content-title"> <html>
{context.title}
</h2>
<div class="content"> <head>
<a href="/">Go Home</a> <BaseHead title={title} description={description} permalink="TODO" />
</div> </head>
<body>
<MainLayout>
<h2 class="content-title">
{title}
</h2>
<div class="content">
<a href="/">Go Home</a>
</div>
</MainLayout>
</body>
</html>

View file

@ -1,73 +1,86 @@
--- ---
import Card from '../components/Card.jsx'; import Card from '../components/Card.jsx';
import BaseHead from '../components/BaseHead.astro';
import MainLayout from '../components/MainLayout.astro';
export const layout = 'layouts/main.astro'; // mocked for now, to be added later
// 1. import {paginate} from 'magicthing';
// mocked for now, to be added later // 2. export default function ({paginate}) {
// 1. import {paginate} from 'magicthing'; function paginate(options) {
// 2. export default function ({paginate}) { if (options.tag === 'guide') {
function paginate(options) { return [
if (options.tag === 'guide') { { title: 'Test guide 1', href: "#" },
return [ { 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 [];
} }
if (options.tag === 'communityGuides') {
export function setup({ context, /* paginate */ }) { return [{ title: 'Test communityGuides', href: "#" }];
return {
context: {
title: 'Guides',
description: "Snowpack's usage and integration guides.",
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,
}),
}
};
} }
return [];
}
let title = 'Guides';
let description = 'Snowpack\'s usage and integration guides.';
let guides;
let communityGuides;
export function setup({ /* paginate */ }) {
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,
});
return {};
}
--- ---
<h2 class="content-title"> <html>
{context.title}
</h2>
<h3 class="content-title"> <head>
Using Snowpack <BaseHead title={title} description={description} permalink="TODO" />
</h3> </head>
<div class="content"> <body>
<ul> <MainLayout>
{context.guides.map((post) => { <h2 class="content-title">
return <li><a href={post.href}>{post.title}</a></li>; {title}
})} </h2>
</ul>
</div>
<br /> <h3 class="content-title">
<br /> Using Snowpack
</h3>
<h3 class="content-title"> <div class="content">
Popular Integration Guides <ul>
</h3> {guides.map((post) => {
return <li><a href={post.href}>{post.title}</a></li>;
})}
</ul>
</div>
<div class="card-grid card-grid-4"> <br />
{context.communityGuides.map((post) => { <br />
return
<Card item={post} />; <h3 class="content-title">
})} Popular Integration Guides
</div> </h3>
<div class="card-grid card-grid-4">
{communityGuides.map((post) => {
return
<Card item={post} />;
})}
</div>
</MainLayout>
</body>
</html>

View file

@ -1,18 +1,19 @@
--- ---
import Menu from '../components/Menu.astro'; import Menu from '../components/Menu.astro';
import Hero from '../components/Hero.astro'; import Hero from '../components/Hero.astro';
import BaseHead from '../components/BaseHead.astro';
import BaseLayout from '../components/BaseLayout.astro';
export const layout = 'layouts/base.astro'; let title = 'Snowpack';
export function setup({ context }) { let description = 'Snowpack is a lightning-fast frontend build tool, designed for the modern web.';
return {};
}
--- ---
<astro:head> <html>
<meta charset="AAA" />
<head>
<style lang="scss"> <style lang="scss">
@use '../../public/css/var' as *; @use '../../public/css/var' as *;
.top { .top {
text-align: left; text-align: left;
} }
@ -56,113 +57,96 @@
margin: 0.5em; margin: 0.5em;
} }
</style> </style>
</astro:head> <BaseHead title={title} description={description} permalink="TODO" />
<Hero bar="{context.title}"></Hero> </head>
<div foo="{context.title}" class="container" style="margin: 0 auto"> <body>
<section class="snow-view__docs is-full is-home"> <BaseLayout>
<aside id="nav-primary" class="snow-view-nav"> <Hero bar="{title}" />
<Menu></Menu> <div foo="{title}" class="container" style="margin: 0 auto">
</aside> <section class="snow-view__docs is-full is-home">
<aside id="nav-primary" class="snow-view-nav">
<Menu></Menu>
</aside>
<article class="snow-view-main"> <article class="snow-view-main">
<div class="content"> <div class="content">
<article class="grid-body"> <article class="grid-body">
<a <a class="img-banner" href="https://osawards.com/javascript/2020" target="_blank"
class="img-banner" rel="noopener noreferrer">
href="https://osawards.com/javascript/2020" <img src="/img/JSAwardWinner.png" alt="2020 JavaScript Open Source Award Winner banner" />
target="_blank" </a>
rel="noopener noreferrer"
>
<img
src="/img/JSAwardWinner.png"
alt="2020 JavaScript Open Source Award Winner banner"
/>
</a>
<div class="content markdown-body feature-list"> <div class="content markdown-body feature-list">
<div class="feature-list-top"> <div class="feature-list-top">
<p> <p>
<strong <strong>Snowpack is a lightning-fast frontend build tool, designed
>Snowpack is a lightning-fast frontend build tool, designed for the modern web.</strong>
for the modern web.</strong It is an alternative to heavier, more complex bundlers like
> webpack or Parcel in your development workflow. Snowpack
It is an alternative to heavier, more complex bundlers like leverages JavaScript's native module system (<a
webpack or Parcel in your development workflow. Snowpack href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">known
leverages JavaScript's native module system (<a as
href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import" ESM</a>) to avoid unnecessary work and stay fast no matter how big
>known as ESM</a your project grows.
>) to avoid unnecessary work and stay fast no matter how big </p>
your project grows. <p>
</p> Once you try it, it's impossible to go back to anything else.
<p> </p>
Once you try it, it's impossible to go back to anything else. </div>
</p>
</div>
<ul class="feature-list-bullets"> <ul class="feature-list-bullets">
<li class="feature-list-bullet"> <li class="feature-list-bullet">
<h3>Instant startup</h3> <h3>Instant startup</h3>
Snowpack's unbundled web development server Snowpack's unbundled web development server
<strong>starts up in 50ms or less</strong> <strong>starts up in 50ms or less</strong>
and stays fast in large projects. and stays fast in large projects.
</li> </li>
<li class="feature-list-bullet"> <li class="feature-list-bullet">
<h3>Build once, cache forever</h3> <h3>Build once, cache forever</h3>
Snowpack never builds the same file twice. Powered by Snowpack never builds the same file twice. Powered by
JavaScripts native module system (ESM) in the browser. JavaScripts native module system (ESM) in the browser.
</li> </li>
<li class="feature-list-bullet"> <li class="feature-list-bullet">
<h3>HMR feat. Fast Refresh</h3> <h3>HMR feat. Fast Refresh</h3>
No refresh required. See changes reflected instantly in the No refresh required. See changes reflected instantly in the
browser with browser with
<a href="/concepts/hot-module-replacement" <a href="/concepts/hot-module-replacement">HMR + Fast Refresh</a>
>HMR + Fast Refresh</a for React, Preact & Svelte.
> </li>
for React, Preact & Svelte. <li class="feature-list-bullet">
</li> <h3>Out-of-the-box support</h3>
<li class="feature-list-bullet"> Enjoy Snowpack's built-in support for JSX, TypeScript, React,
<h3>Out-of-the-box support</h3> Preact, CSS Modules
Enjoy Snowpack's built-in support for JSX, TypeScript, React, <a href="/reference/supported-files">and more.</a>
Preact, CSS Modules </li>
<a href="/reference/supported-files">and more.</a> <li class="feature-list-bullet">
</li> <h3>Optimize for production</h3>
<li class="feature-list-bullet"> Build for production with built-in optimizations and plugin
<h3>Optimize for production</h3> support for your favorite bundlers.
Build for production with built-in optimizations and plugin </li>
support for your favorite bundlers. <li class="feature-list-bullet">
</li> <h3>Plugins? Plugins!</h3>
<li class="feature-list-bullet"> Babel? Sass? MDX? Browse the entire
<h3>Plugins? Plugins!</h3> <a href="/plugins">Snowpack Plugin Catalog</a>
Babel? Sass? MDX? Browse the entire to connect your favorite build tool (or
<a href="/plugins">Snowpack Plugin Catalog</a> <a href="/reference/plugins">create your own!</a>)
to connect your favorite build tool (or </li>
<a href="/reference/plugins">create your own!</a>) </ul>
</li>
</ul>
<div class="feature-list-buttons"> <div class="feature-list-buttons">
<a <a href="/tutorials/quick-start" class="button button-primary feature-list-button">Get started</a>
href="/tutorials/quick-start" <a href="/concepts/how-snowpack-works" class="button feature-list-button">Learn more</a>
class="button button-primary feature-list-button" </div>
>Get started</a </div>
> </article>
<a
href="/concepts/how-snowpack-works"
class="button feature-list-button"
>Learn more</a
>
</div>
</div> </div>
</article> </article>
</div> </section>
</article> </div>
</section> </BaseLayout>
</div> <!-- 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>
<!-- Place this tag in your head or just before your close body tag. --> </html>
<script
async="async"
defer="defer"
src="https://buttons.github.io/buttons.js"
></script>

View file

@ -1,78 +1,86 @@
--- ---
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';
import BaseHead from '../components/BaseHead.astro';
import MainLayout from '../components/MainLayout.astro';
export const layout = 'layouts/main.astro'; // Using Snowpack? Want to be featured on snowpack.dev?
// Add your project, organization, or company to the end of this list!
import news from '../data/news.json';
import users from '../data/users.json';
import news from '../data/news.json'; let title = 'Community & News';
import users from '../data/users.json'; let description = 'Snowpack community news and companies that use Snowpack.';
let pokemonData;
export async function setup({ context, request, fetch }) { export async function setup({ context, request, fetch }) {
const pokemonData = await fetch(`https://pokeapi.co/api/v2/pokemon/ditto`); const pokemonDataReq = await fetch(`https://pokeapi.co/api/v2/pokemon/ditto`);
return { pokemonData = await pokemonDataReq.json();
context: { return {};
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,
pokemonData: await pokemonData.json(),
}
}
}
--- ---
<NewsTitle title={context.title} /> <html>
<p> <head>
Get the latest news, blog posts, and tutorials on Snowpack. <a href="/feed.xml">Also available via RSS.</a> <BaseHead title={title} description={description} permalink="TODO" />
</p> </head>
<p> <body>
Got something that you think we should feature? <MainLayout>
<a href="https://github.com/snowpackjs/snowpack/edit/main/www/_data/news.js">Submit it!</a> <NewsTitle title={title} />
</p> <p>
Get the latest news, blog posts, and tutorials on Snowpack. <a href="/feed.xml">Also available via RSS.</a>
</p>
<p> <p>
In case you're curious, the best pokemon is <strong>{context.pokemonData.name}.</strong> Got something that you think we should feature?
</p> <a href="https://github.com/snowpackjs/snowpack/edit/main/www/_data/news.js">Submit it!</a>
</p>
<div class="card-grid card-grid-3"> <p>
<article class="discord-banner"> In case you're curious, the best pokemon is <strong>{pokemonData.name}.</strong>
<a href="https://discord.gg/snowpack" style="flex-shrink: 0; height: 48px;"><img alt="Join us on Discord!" </p>
src="https://img.shields.io/discord/712696926406967308.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2"
style="height: 48px; border: none; margin-right: 1rem; filter: brightness(1.2) contrast(1.5);" /></a>
<div>Join us on Discord to discuss Snowpack, meet other developers in our community, and show off what youre
working on!</div>
</article>
{context.news.reverse().map((item: any) => <div class="card-grid card-grid-3">
<Card:dynamic item={item} />)} <article class="discord-banner">
</div> <a href="https://discord.gg/snowpack" style="flex-shrink: 0; height: 48px;"><img alt="Join us on Discord!"
src="https://img.shields.io/discord/712696926406967308.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2"
style="height: 48px; border: none; margin-right: 1rem; filter: brightness(1.2) contrast(1.5);" /></a>
<div>Join us on Discord to discuss Snowpack, meet other developers in our community, and show off what youre
working on!</div>
</article>
<div class="content"> {news.reverse().map((item: any) =>
<Card:dynamic item={item} />)}
</div>
<h3>Who's Using Snowpack?</h3> <div class="content">
<div class="company-logos"> <h3>Who's Using Snowpack?</h3>
{context.users.map((user) => <div class="company-logos">
<CompanyLogo user={user} />)}
<a href="https://github.com/snowpackjs/snowpack/edit/main/www/_template/news.md" target="_blank" {users.map((user) =>
title="Add Your Project/Company!" class="add-company-button"> <CompanyLogo user={user} />)}
<svg style="height: 22px; margin-right: 8px;" aria-hidden="true" focusable="false" data-prefix="fas"
data-icon="plus" class="company-logo" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor"
d="M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z">
</path>
</svg>
Add your logo
</a>
</div>
<NewsAssets /> <a href="https://github.com/snowpackjs/snowpack/edit/main/www/_template/news.md" target="_blank"
</div> title="Add Your Project/Company!" class="add-company-button">
<svg style="height: 22px; margin-right: 8px;" aria-hidden="true" focusable="false" data-prefix="fas"
data-icon="plus" class="company-logo" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor"
d="M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z">
</path>
</svg>
Add your logo
</a>
</div>
<NewsAssets />
</div>
</MainLayout>
</body>
</html>

View file

@ -1,70 +1,72 @@
--- ---
import news from '../data/news.json'; import PluginSearchPage from '../components/PluginSearchPage.jsx';
import users from '../data/users.json'; import BaseHead from '../components/BaseHead.astro';
import PluginSearchPage from '../components/PluginSearchPage.jsx'; import MainLayout from '../components/MainLayout.astro';
export const layout = 'layouts/main.astro'; let title = 'The Snowpack Plugin Catalog';
let description = 'Snowpack plugins allow for configuration-minimal tooling integration.';
export function setup({ context }) {
return {
context: {
title: 'The Snowpack Plugin Catalog',
description:
'Snowpack plugins allow for configuration-minimal tooling integration.',
},
};
}
--- ---
<style lang="scss"> <html>
.intro {
margin-top: 1rem;
margin-bottom: 1rem;
line-height: 1.5;
text-align: left;
a { <head>
<BaseHead title={title} description={description} permalink="TODO" />
<style lang="scss">
.intro {
margin-top: 1rem;
margin-bottom: 1rem;
line-height: 1.5;
text-align: left;
a {
color: #2e5e82;
}
}
.subheading {
margin-top: -2.5rem;
margin-bottom: 0;
color: #2e5e82; color: #2e5e82;
font-weight: 500;
font-size: 1em;
font-family: 'Overpass', sans-serif;
letter-spacing: -0.02em;
text-align: left;
@media (min-width: 600px) {
font-size: 1.5em;
}
} }
}
.subheading { .zero-heading {
margin-top: -2.5rem; margin-top: 1.5rem;
margin-bottom: 0; margin-bottom: 1.5rem;
color: #2e5e82; font-weight: 700;
font-weight: 500; font-size: 1.4em;
font-size: 1em; text-align: left;
font-family: 'Overpass', sans-serif;
letter-spacing: -0.02em;
text-align: left;
@media (min-width: 600px) { @media (min-width: 600px) {
font-size: 1.5em; font-size: 1.5em;
}
} }
} </style>
</head>
.zero-heading { <body>
margin-top: 1.5rem; <MainLayout>
margin-bottom: 1.5rem; <h2 class="content-title">{ title }</h2>
font-weight: 700;
font-size: 1.4em;
text-align: left;
@media (min-width: 600px) { <h3 class="pluginPage-subheading">
font-size: 1.5em; Customize Snowpack with optimized build plugins.
} </h3>
} <p class="pluginPage-intro">
</style> To learn more about our plugin system, check out the
<a href="/reference/plugins">Plugin API.</a><br />Can't find what you need?
<a href="/reference/plugins">Creating your own plugin is easy!</a>
</p>
<h2 class="content-title">{ context.title }</h2> <PluginSearchPage:dynamic />
</MainLayout>
</body>
<h3 class="pluginPage-subheading"> </html>
Customize Snowpack with optimized build plugins.
</h3>
<p class="pluginPage-intro">
To learn more about our plugin system, check out the
<a href="/reference/plugins">Plugin API.</a><br />Can't find what you need?
<a href="/reference/plugins">Creating your own plugin is easy!</a>
</p>
<PluginSearchPage:dynamic />

View file

@ -1,23 +1,34 @@
--- ---
import Subnav from '../components/Subnav.astro'; import Subnav from '../components/Subnav.astro';
import { content as Menu } from '../components/Menu.astro'; import Menu from '../components/Menu.astro';
// import contentful from 'skypack:contentful'; import BaseHead from '../components/BaseHead.astro';
import BaseLayout from '../components/BaseLayout.astro';
// import contentful from 'skypack:contentful';
export default async function ({ params }) { let title = 'Community & News';
const entry = await contentful.getEntry(params.slug); let description = 'Snowpack community news and companies that use Snowpack.';
return { title: entry.fields.title, description: entry.fields.description, layout: 'layouts/base.astro', props: { entry } }; let entry;
}
export default async function ({ params }) {
entry = await contentful.getEntry(params.slug);
return { title: entry.fields.title, description: entry.fields.description, layout: 'layouts/base.astro', props: { entry } };
}
--- ---
<div class="container"> <html>
<section class="snow-view__docs has-subnav">
<head>
<BaseHead title={title} description={description} permalink="TODO" />
</head>
<body>
<MainLayout>
<div class="container">
<section class="snow-view__docs has-subnav">
<aside id="nav-primary" class="snow-view-nav"> <aside id="nav-primary" class="snow-view-nav">
<Menu /> <Menu />
</aside> </aside>
<Subnav title={context.title} headers={context.content.headers} />
<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">
@ -30,6 +41,9 @@
</div> </div>
</div> </div>
</article> </article>
</section>
</div>
</MainLayout>
</body>
</section> </html>
</div>

View file

@ -11,7 +11,7 @@ export interface AstroConfig {
dist: string; dist: string;
projectRoot: URL; projectRoot: URL;
astroRoot: URL; astroRoot: URL;
extensions?: Record<string, ValidExtensionPlugins> extensions?: Record<string, ValidExtensionPlugins>;
} }
export interface JsxItem { export interface JsxItem {
@ -21,7 +21,7 @@ export interface JsxItem {
export interface TransformResult { export interface TransformResult {
script: string; script: string;
head: JsxItem | undefined; props: string[];
items: JsxItem[]; items: JsxItem[];
} }

View file

@ -99,20 +99,17 @@ const defaultExtensions: Readonly<Record<string, ValidExtensionPlugins>> = {
'.astro': 'astro', '.astro': 'astro',
'.jsx': 'react', '.jsx': 'react',
'.vue': 'vue', '.vue': 'vue',
'.svelte': 'svelte' '.svelte': 'svelte',
}; };
function getComponentWrapper(_name: string, { type, url }: ComponentInfo, compileOptions: CompileOptions) { function getComponentWrapper(_name: string, { type, url }: ComponentInfo, compileOptions: CompileOptions) {
const { const { resolve, extensions = defaultExtensions } = compileOptions;
resolve,
extensions = defaultExtensions
} = compileOptions;
const [name, kind] = _name.split(':'); const [name, kind] = _name.split(':');
const plugin = extensions[type] || defaultExtensions[type]; const plugin = extensions[type] || defaultExtensions[type];
if(!plugin) { if (!plugin) {
throw new Error(`No supported plugin found for extension ${type}`); throw new Error(`No supported plugin found for extension ${type}`);
} }
@ -142,7 +139,9 @@ function getComponentWrapper(_name: string, { type, url }: ComponentInfo, compil
case 'react': { case 'react': {
if (kind === 'dynamic') { if (kind === 'dynamic') {
return { return {
wrapper: `__react_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve('react')}', '${resolve('react-dom')}')`, wrapper: `__react_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve(
'react'
)}', '${resolve('react-dom')}')`,
wrapperImport: `import {__react_dynamic} from '${internalImport('render/react.js')}';`, wrapperImport: `import {__react_dynamic} from '${internalImport('render/react.js')}';`,
}; };
} else { } else {
@ -215,6 +214,9 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
// Compile scripts as TypeScript, always // Compile scripts as TypeScript, always
const script = compileScriptSafe(ast.module ? ast.module.content : ''); const script = compileScriptSafe(ast.module ? ast.module.content : '');
// Collect all exported variables for props
const scannedExports = eslexer.parse(script)[1].filter((n) => n !== 'setup' && n !== 'layout');
// 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');
const components = Object.fromEntries( const components = Object.fromEntries(
@ -380,7 +382,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
return { return {
script: script + '\n' + Array.from(additionalImports).join('\n'), script: script + '\n' + Array.from(additionalImports).join('\n'),
head: headItem,
items, items,
props: scannedExports,
}; };
} }

View file

@ -72,4 +72,4 @@ export default async function (astroConfig: AstroConfig) {
function formatErrorForBrowser(error: Error) { function formatErrorForBrowser(error: Error) {
// TODO make this pretty. // TODO make this pretty.
return error.toString(); return error.toString();
} }

View file

@ -3,13 +3,7 @@ import ReactDOMServer from 'react-dom/server';
export function __react_static(ReactComponent: any) { export function __react_static(ReactComponent: any) {
return (attrs: Record<string, any>, ...children: any): string => { return (attrs: Record<string, any>, ...children: any): string => {
let html = ReactDOMServer.renderToString( let html = ReactDOMServer.renderToString(React.createElement(ReactComponent, attrs, children));
React.createElement(
ReactComponent,
attrs,
children
)
);
return html; return html;
}; };
} }

View file

@ -32,4 +32,4 @@ const plugin: HtmlExtension = {
}, },
}; };
export { plugin as encodeMarkdown }; export { plugin as encodeMarkdown };

View file

@ -69,6 +69,7 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
href: fullurl.toString(), href: fullurl.toString(),
}, },
children: [], children: [],
props: {},
})) as string; })) as string;
return { return {
@ -103,7 +104,7 @@ export async function createRuntime(astroConfig: AstroConfig, logging: LogOption
// Workaround for SKY-251 // Workaround for SKY-251
const astroPlugOptions: { const astroPlugOptions: {
resolve?: (s: string) => string; resolve?: (s: string) => string;
extensions?: Record<string, string> extensions?: Record<string, string>;
} = { extensions }; } = { extensions };
if (existsSync(new URL('./package-lock.json', projectRoot))) { if (existsSync(new URL('./package-lock.json', projectRoot))) {
const pkgLockStr = await readFile(new URL('./package-lock.json', projectRoot), 'utf-8'); const pkgLockStr = await readFile(new URL('./package-lock.json', projectRoot), 'utf-8');
@ -128,10 +129,7 @@ export async function createRuntime(astroConfig: AstroConfig, logging: LogOption
}, },
packageOptions: { packageOptions: {
knownEntrypoints: ['preact-render-to-string'], knownEntrypoints: ['preact-render-to-string'],
external: [ external: ['@vue/server-renderer', 'node-fetch'],
'@vue/server-renderer',
'node-fetch'
],
}, },
}); });
const snowpack = await startSnowpackServer({ const snowpack = await startSnowpackServer({

View file

@ -52,7 +52,7 @@ 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> {
const { data: _frontmatterData, content } = matter(contents); const { data: frontmatterData, content } = matter(contents);
const { headers, headersExtension } = createMarkdownHeadersCollector(); const { headers, headersExtension } = createMarkdownHeadersCollector();
const mdHtml = micromark(content, { const mdHtml = micromark(content, {
allowDangerousHtml: true, allowDangerousHtml: true,
@ -60,31 +60,27 @@ async function convertMdToJsx(
htmlExtensions: [gfmHtml, encodeMarkdown, headersExtension], htmlExtensions: [gfmHtml, encodeMarkdown, headersExtension],
}); });
const setupContext = { // TODO: Warn if reserved word is used in "frontmatterData"
..._frontmatterData, const contentData: any = {
content: { ...frontmatterData,
frontmatter: _frontmatterData, headers,
headers, source: content,
source: content, html: mdHtml,
html: mdHtml,
},
}; };
let imports = ''; let imports = '';
for(let [ComponentName, specifier] of Object.entries(_frontmatterData.import || {})) { for (let [ComponentName, specifier] of Object.entries(frontmatterData.import || {})) {
imports += `import ${ComponentName} from '${specifier}';\n`; imports += `import ${ComponentName} from '${specifier}';\n`;
} }
// </script> can't be anywhere inside of a JS string, otherwise the HTML parser fails. // </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. // Break it up here so that the HTML parser won't detect it.
const stringifiedSetupContext = JSON.stringify(setupContext).replace(/\<\/script\>/g, `</scrip" + "t>`); const stringifiedSetupContext = JSON.stringify(contentData).replace(/\<\/script\>/g, `</scrip" + "t>`);
const raw = `--- const raw = `---
${imports} ${imports}
${_frontmatterData.layout ? `export const layout = ${JSON.stringify(_frontmatterData.layout)};` : ''} ${frontmatterData.layout ? `const __layout = ${JSON.stringify(frontmatterData.layout)};` : ''}
export function setup({context}) { const __content = ${stringifiedSetupContext};
return {context: ${stringifiedSetupContext} };
}
--- ---
<section>${mdHtml}</section>`; <section>${mdHtml}</section>`;
@ -115,11 +111,10 @@ export async function compileComponent(
{ 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;
const headItemJsx = !headItem ? 'null' : headItem.jsx;
// sort <style> tags first // sort <style> tags first
// TODO: remove these and inject in <head> // TODO: remove these and inject in <head>
const isPage = path.extname(filename) === '.md' || sourceJsx.items.some((item) => item.name === 'html');
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
@ -127,61 +122,47 @@ export async function compileComponent(
// <script astro></script> // <script astro></script>
${sourceJsx.script} ${sourceJsx.script}
// \`__render()\`: Render the contents of the Astro module. "<slot:*>" elements are not // \`__render()\`: Render the contents of the Astro module.
// included (see below).
import { h, Fragment } from '${internalImport('h.js')}'; import { h, Fragment } from '${internalImport('h.js')}';
export function __slothead(children, context) { return h(Fragment, null, ${headItemJsx}); } function __render(props, ...children) {
function __render(props, children, context) { return h(Fragment, null, ${sourceJsx.items.map(({ jsx }) => jsx).join(',')}); } ${sourceJsx.props.map((p) => `${p} = props.${p} ?? ${p};`).join('\n')}
return h(Fragment, null, ${sourceJsx.items.map(({ jsx }) => jsx).join(',')});
}
export default __render; export default __render;
`; `;
if (headItemJsx) { if (isPage) {
modJsx += ` modJsx += `
// \`__renderPage()\`: Render the contents of the Astro module as a page. This is a special flow, // \`__renderPage()\`: Render the contents of the Astro module as a page. This is a special flow,
// triggered by loading a component directly by URL. // triggered by loading a component directly by URL.
// If the page exports a defined "layout", then load + render those first. "context", "astro:head", export async function __renderPage({request, children, props}) {
// and "slot:body" should all inherit from parent layouts, merging together in the correct order.
export async function __renderPage({request, children}) {
const currentChild = { const currentChild = {
__slothead,
__render,
setup: typeof setup === 'undefined' ? (passthrough) => passthrough : setup, setup: typeof setup === 'undefined' ? (passthrough) => passthrough : setup,
layout: typeof layout === 'undefined' ? undefined : layout, layout: typeof __layout === 'undefined' ? undefined : __layout,
content: typeof __content === 'undefined' ? undefined : __content,
__render,
}; };
// find all layouts, going up the layout chain. const fetch = (await import('node-fetch')).default;
await currentChild.setup({request, fetch});
const childBodyResult = await currentChild.__render(props, children);
// find layout, if one was given.
if (currentChild.layout) { if (currentChild.layout) {
const layoutComponent = (await import('/_astro/layouts/' + layout.replace(/.*layouts\\//, "").replace(/\.astro$/, '.js'))); const layoutComponent = (await import('/_astro/layouts/' + currentChild.layout.replace(/.*layouts\\//, "").replace(/\.astro$/, '.js')));
return layoutComponent.__renderPage({ return layoutComponent.__renderPage({
request, request,
children: [currentChild, ...children], props: {content: currentChild.content},
children: [childBodyResult],
}); });
} }
const isRoot = true;
const merge = (await import('deepmerge')).default;
const fetch = (await import('node-fetch')).default;
// call all children setup scripts, in order, and return. return childBodyResult;
let mergedContext = {};
for (const child of [currentChild, ...children]) {
const childSetupResult = await child.setup({request, fetch, 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([headResult], mergedContext);
bodyResult = await child.__render(undefined, [bodyResult], mergedContext);
}
return h(Fragment, null, [
h("head", null, currentChild.__slothead([headResult], mergedContext)),
h("body", null, currentChild.__render(undefined, [bodyResult], mergedContext)),
]);
};\n`; };\n`;
} else {
modJsx += `
export async function __renderPage() { throw new Error("No <html> page element found!"); }\n`;
} }
return { return {

View file

@ -1,13 +1,14 @@
--- ---
export function setup() { export function setup() {
return { return {props: {}}
props: {}
}
} }
--- ---
<astro:head> <html>
<!-- Head Stuff --> <head>
</astro:head> <!-- Head Stuff -->
</head>
<h1>Hello world!</h1> <body>
<h1>Hello world!</h1>
</body>
</html>

View file

@ -1,3 +1,10 @@
<div class="container"> <html>
<slot></slot> <head>
</div> <!-- Head Stuff -->
</head>
<body>
<div class="container">
<slot></slot>
</div>
</body>
</html>

View file

@ -1,13 +1,14 @@
--- ---
export function setup() { export function setup() {
return { return {props: {}}
props: {}
}
} }
--- ---
<astro:head> <html>
<!-- Head Stuff --> <head>
</astro:head> <!-- Head Stuff -->
</head>
<h1>Hello world!</h1> <body>
<h1>Hello world!</h1>
</body>
</html>

View file

@ -2,9 +2,11 @@
import Hello from '../components/Hello.jsx'; import Hello from '../components/Hello.jsx';
--- ---
<astro:head> <html>
<!-- Head Stuff --> <head>
</astro:head> <!-- Head Stuff -->
</head>
<h1>My page</h1> <body>
<Hello name="world" /> <Hello name="world" />
</body>
</html>

View file

@ -72,7 +72,7 @@ SnowpackDev('Can load every page', async () => {
} }
const result = await runtime.load(pathname); const result = await runtime.load(pathname);
if (result.statusCode === 500) { if (result.statusCode === 500) {
failed.push(result); failed.push({...result, pathname});
continue; continue;
} }
assert.equal(result.statusCode, 200, `Loading ${pathname}`); assert.equal(result.statusCode, 200, `Loading ${pathname}`);