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,14 +1,15 @@
--- ---
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>
<head>
<style> <style>
.cover-wrapper { .cover-wrapper {
width: 100%; width: 100%;
@ -47,10 +48,15 @@
} }
} }
</style> </style>
<BaseHead title={content.title} description={content.description} permalink="TODO" />
</head>
<body>
<BaseLayout>
<div class="cover-wrapper"> <div class="cover-wrapper">
<img class="cover-blur" src={context.cover} alt=""/> <img class="cover-blur" src={content.cover} alt=""/>
<img class="cover" src={context.cover} alt=""/> <img class="cover" src={content.cover} alt=""/>
</div> </div>
<div class="container"> <div class="container">
@ -63,16 +69,20 @@
<article class="snow-view-main"> <article class="snow-view-main">
<div class="content"> <div class="content">
<h2 class="content-title"> <h2 class="content-title">
{context.title} {content.title}
</h2> </h2>
<div class="content-layout"> <div class="content-layout">
<div class="content-body"> <div class="content-body">
<slot></slot> {content.body}
</div> </div>
</div> </div>
</div> </div>
</article> </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,16 +1,20 @@
--- ---
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: {
}
};
}
--- ---
<html>
<head>
<BaseHead title={content.title} description={content.description} permalink="TODO" />
</head>
<body>
<BaseLayout>
<div class="container"> <div class="container">
<section class="snow-view__docs has-subnav"> <section class="snow-view__docs has-subnav">
@ -18,12 +22,12 @@
<Menu /> <Menu />
</aside> </aside>
<Subnav title={context.title} headers={context.content.headers} /> <Subnav title={content.title} headers={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">
{context.title} {content.title}
</h2> </h2>
<div class="content-layout"> <div class="content-layout">
<div class="content-body"> <div class="content-body">
@ -35,3 +39,7 @@
</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,15 +1,14 @@
--- ---
import BaseHead from '../components/BaseHead.astro';
import BaseLayout from '../components/BaseLayout.astro';
import { format as formatDate, parseISO } from 'date-fns'; import { format as formatDate, parseISO } from 'date-fns';
export const layout = 'layouts/base.astro';
export function setup({ context }) { export let content: any;
return {};
}
--- ---
<astro:head> <html>
<link rel="stylesheet" href="/css/legacy-post.css" />
</astro:head>
<head>
<style> <style>
.markdown-body img, .markdown-body img,
.markdown-body video, .markdown-body video,
@ -87,6 +86,12 @@
opacity: 1; opacity: 1;
} }
</style> </style>
<BaseHead title={content.title} description={content.description} permalink="TODO" />
<link rel="stylesheet" href="/css/legacy-post.css" />
</head>
<body>
<BaseLayout>
<div class="grid-extra-space"> <div class="grid-extra-space">
<div class="grid-body-header"> <div class="grid-body-header">
@ -100,13 +105,13 @@
</g> </g>
</g> </g>
</svg> </svg>
<h1 class="header-snowpack">{context.title}</h1> <h1 class="header-snowpack">{content.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>
</div> </div>
@ -125,3 +130,7 @@
<slot></slot> <slot></slot>
</article> </article>
</div> </div>
</BaseLayout>
</body>
</html>

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',
}
};
}
--- ---
<html>
<head>
<BaseHead title={title} description={description} permalink="TODO" />
</head>
<body>
<MainLayout>
<h2 class="content-title"> <h2 class="content-title">
{context.title} {title}
</h2> </h2>
<div class="content"> <div class="content">
<a href="/">Go Home</a> <a href="/">Go Home</a>
</div> </div>
</MainLayout>
</body>
</html>

View file

@ -1,7 +1,7 @@
--- ---
import Card from '../components/Card.jsx'; import Card from '../components/Card.jsx';
import BaseHead from '../components/BaseHead.astro';
export const layout = 'layouts/main.astro'; import MainLayout from '../components/MainLayout.astro';
// mocked for now, to be added later // mocked for now, to be added later
// 1. import {paginate} from 'magicthing'; // 1. import {paginate} from 'magicthing';
@ -19,31 +19,40 @@
return []; return [];
} }
export function setup({ context, /* paginate */ }) { let title = 'Guides';
return { let description = 'Snowpack\'s usage and integration guides.';
context: { let guides;
title: 'Guides', let communityGuides;
description: "Snowpack's usage and integration guides.",
guides: paginate({
export function setup({ /* 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,
}), });
} return {};
};
} }
--- ---
<html>
<head>
<BaseHead title={title} description={description} permalink="TODO" />
</head>
<body>
<MainLayout>
<h2 class="content-title"> <h2 class="content-title">
{context.title} {title}
</h2> </h2>
<h3 class="content-title"> <h3 class="content-title">
@ -52,7 +61,7 @@
<div class="content"> <div class="content">
<ul> <ul>
{context.guides.map((post) => { {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>
@ -66,8 +75,12 @@
</h3> </h3>
<div class="card-grid card-grid-4"> <div class="card-grid card-grid-4">
{context.communityGuides.map((post) => { {communityGuides.map((post) => {
return return
<Card item={post} />; <Card item={post} />;
})} })}
</div> </div>
</MainLayout>
</body>
</html>

View file

@ -1,15 +1,16 @@
--- ---
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 *;
@ -56,10 +57,13 @@
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>
<BaseLayout>
<Hero bar="{title}" />
<div foo="{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">
<Menu></Menu> <Menu></Menu>
@ -68,31 +72,22 @@
<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"
rel="noopener noreferrer"
>
<img
src="/img/JSAwardWinner.png"
alt="2020 JavaScript Open Source Award Winner banner"
/>
</a> </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 It is an alternative to heavier, more complex bundlers like
webpack or Parcel in your development workflow. Snowpack webpack or Parcel in your development workflow. Snowpack
leverages JavaScript's native module system (<a leverages JavaScript's native module system (<a
href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">known
>known as ESM</a as
>) to avoid unnecessary work and stay fast no matter how big ESM</a>) to avoid unnecessary work and stay fast no matter how big
your project grows. your project grows.
</p> </p>
<p> <p>
@ -116,9 +111,7 @@
<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. for React, Preact & Svelte.
</li> </li>
<li class="feature-list-bullet"> <li class="feature-list-bullet">
@ -142,16 +135,8 @@
</ul> </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"
>Get started</a
>
<a
href="/concepts/how-snowpack-works"
class="button feature-list-button"
>Learn more</a
>
</div> </div>
</div> </div>
</article> </article>
@ -159,10 +144,9 @@
</article> </article>
</section> </section>
</div> </div>
</BaseLayout>
<!-- 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 <script async="async" defer="defer" src="https://buttons.github.io/buttons.js"></script>
async="async" </body>
defer="defer"
src="https://buttons.github.io/buttons.js" </html>
></script>

View file

@ -3,30 +3,34 @@
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 news from '../data/news.json';
import users from '../data/users.json'; import users from '../data/users.json';
let title = 'Community & News';
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>
<head>
<BaseHead title={title} description={description} permalink="TODO" />
</head>
<body>
<MainLayout>
<NewsTitle title={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>
</p> </p>
@ -37,7 +41,7 @@
</p> </p>
<p> <p>
In case you're curious, the best pokemon is <strong>{context.pokemonData.name}.</strong> In case you're curious, the best pokemon is <strong>{pokemonData.name}.</strong>
</p> </p>
<div class="card-grid card-grid-3"> <div class="card-grid card-grid-3">
@ -49,7 +53,7 @@
working on!</div> working on!</div>
</article> </article>
{context.news.reverse().map((item: any) => {news.reverse().map((item: any) =>
<Card:dynamic item={item} />)} <Card:dynamic item={item} />)}
</div> </div>
@ -59,7 +63,7 @@
<div class="company-logos"> <div class="company-logos">
{context.users.map((user) => {users.map((user) =>
<CompanyLogo user={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"
@ -76,3 +80,7 @@
<NewsAssets /> <NewsAssets />
</div> </div>
</MainLayout>
</body>
</html>

View file

@ -1,21 +1,16 @@
--- ---
import news from '../data/news.json';
import users from '../data/users.json';
import PluginSearchPage from '../components/PluginSearchPage.jsx'; import PluginSearchPage from '../components/PluginSearchPage.jsx';
import BaseHead from '../components/BaseHead.astro';
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.',
},
};
}
--- ---
<html>
<head>
<BaseHead title={title} description={description} permalink="TODO" />
<style lang="scss"> <style lang="scss">
.intro { .intro {
margin-top: 1rem; margin-top: 1rem;
@ -55,8 +50,11 @@
} }
} }
</style> </style>
</head>
<h2 class="content-title">{ context.title }</h2> <body>
<MainLayout>
<h2 class="content-title">{ title }</h2>
<h3 class="pluginPage-subheading"> <h3 class="pluginPage-subheading">
Customize Snowpack with optimized build plugins. Customize Snowpack with optimized build plugins.
@ -68,3 +66,7 @@
</p> </p>
<PluginSearchPage:dynamic /> <PluginSearchPage:dynamic />
</MainLayout>
</body>
</html>

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 BaseHead from '../components/BaseHead.astro';
import BaseLayout from '../components/BaseLayout.astro';
// import contentful from 'skypack:contentful'; // import contentful from 'skypack:contentful';
let title = 'Community & News';
let description = 'Snowpack community news and companies that use Snowpack.';
let entry;
export default async function ({ params }) { export default async function ({ params }) {
const entry = await contentful.getEntry(params.slug); entry = await contentful.getEntry(params.slug);
return { title: entry.fields.title, description: entry.fields.description, layout: 'layouts/base.astro', props: { entry } }; return { title: entry.fields.title, description: entry.fields.description, layout: 'layouts/base.astro', props: { entry } };
} }
--- ---
<html>
<head>
<BaseHead title={title} description={description} permalink="TODO" />
</head>
<body>
<MainLayout>
<div class="container"> <div class="container">
<section class="snow-view__docs has-subnav"> <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> </section>
</div> </div>
</MainLayout>
</body>
</html>

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,14 +99,11 @@ 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(':');
@ -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

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

@ -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; return childBodyResult;
const merge = (await import('deepmerge')).default;
const fetch = (await import('node-fetch')).default;
// call all children setup scripts, in order, and return.
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>
<!-- Head Stuff --> <!-- Head Stuff -->
</astro:head> </head>
<body>
<h1>Hello world!</h1> <h1>Hello world!</h1>
</body>
</html>

View file

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

View file

@ -1,13 +1,14 @@
--- ---
export function setup() { export function setup() {
return { return {props: {}}
props: {}
}
} }
--- ---
<astro:head> <html>
<head>
<!-- Head Stuff --> <!-- Head Stuff -->
</astro:head> </head>
<body>
<h1>Hello world!</h1> <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>
<!-- Head Stuff --> <!-- Head Stuff -->
</astro:head> </head>
<body>
<h1>My page</h1>
<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}`);