summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Nate Moore <natemoo-re@users.noreply.github.com> 2021-06-24 17:48:24 -0500
committerGravatar GitHub <noreply@github.com> 2021-06-24 17:48:24 -0500
commita136c85e6b2b0445e48184595b2696994621c8f1 (patch)
tree4d06743cf5b0e3f8f5dabcd1c8ae9e8b9b4557b2
parentbc9e0f180ccec7d48fde49c857188543e007bf14 (diff)
downloadastro-a136c85e6b2b0445e48184595b2696994621c8f1.tar.gz
astro-a136c85e6b2b0445e48184595b2696994621c8f1.tar.zst
astro-a136c85e6b2b0445e48184595b2696994621c8f1.zip
New Props API (#515)
* wip: update props api * feat(#139, #309): enable new props api * chore: migrate examples to new props API * docs: update syntax guide for new props API * chore: update examples to new props API * chore: update docs to new Props API * fix: hide __astroInternal from `Astro.props` consumers * chore: remove scratchpad file * chore: fix script error * test: fix failing collection tests * fix: set __astroInternal to `enumerable: false` * chore: add changeset * feat: warn users using old props api
-rw-r--r--.changeset/rich-starfishes-begin.md63
-rw-r--r--docs-www/src/components/Note.astro7
-rw-r--r--docs-www/src/layouts/Main.astro2
-rw-r--r--docs/api.md2
-rw-r--r--docs/collections.md10
-rw-r--r--docs/markdown.md2
-rw-r--r--docs/styling.md2
-rw-r--r--docs/syntax.md23
-rw-r--r--examples/astro-markdown/src/layouts/main.astro1
-rw-r--r--examples/blog/src/components/MainHead.astro19
-rw-r--r--examples/blog/src/components/Nav.astro7
-rw-r--r--examples/blog/src/components/Pagination.astro8
-rw-r--r--examples/blog/src/components/PostPreview.astro7
-rw-r--r--examples/blog/src/layouts/post.astro5
-rw-r--r--examples/blog/src/pages/$author.astro3
-rw-r--r--examples/blog/src/pages/$posts.astro2
-rw-r--r--examples/docs/src/components/Note.astro7
-rw-r--r--examples/docs/src/layouts/Main.astro2
-rw-r--r--examples/portfolio/src/components/MainHead.astro2
-rw-r--r--examples/portfolio/src/layouts/project.astro2
-rw-r--r--examples/portfolio/src/pages/$projects.astro2
-rw-r--r--examples/snowpack/src/components/BaseHead.astro9
-rw-r--r--examples/snowpack/src/components/Button.astro2
-rw-r--r--examples/snowpack/src/components/Nav.astro5
-rw-r--r--examples/snowpack/src/components/PokemonLookup.astro5
-rw-r--r--examples/snowpack/src/components/Subnav.astro9
-rw-r--r--examples/snowpack/src/layouts/content-with-cover.astro2
-rw-r--r--examples/snowpack/src/layouts/content.astro2
-rw-r--r--examples/snowpack/src/layouts/post.astro2
-rw-r--r--packages/astro/components/Markdown.astro3
-rw-r--r--packages/astro/components/Prism.astro5
-rw-r--r--packages/astro/src/@types/astro.ts1
-rw-r--r--packages/astro/src/compiler/codegen/index.ts56
-rw-r--r--packages/astro/src/compiler/index.ts24
-rw-r--r--packages/astro/src/runtime.ts2
-rw-r--r--packages/astro/test/fixtures/astro-basic/src/layouts/base.astro4
-rw-r--r--packages/astro/test/fixtures/astro-collection/src/pages/$grouped.astro4
-rw-r--r--packages/astro/test/fixtures/astro-collection/src/pages/$individual.astro4
-rw-r--r--packages/astro/test/fixtures/astro-collection/src/pages/$nested.astro2
-rw-r--r--packages/astro/test/fixtures/astro-collection/src/pages/$paginated.astro2
-rw-r--r--packages/astro/test/fixtures/astro-collection/src/pages/$remote.astro2
-rw-r--r--packages/astro/test/fixtures/astro-collection/src/pages/$shallow.astro2
-rw-r--r--packages/astro/test/fixtures/astro-global/src/layouts/post.astro2
-rw-r--r--packages/astro/test/fixtures/astro-global/src/pages/$posts.astro2
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/code.astro4
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/complex.astro4
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/deep.astro4
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/post.astro4
-rw-r--r--packages/astro/test/fixtures/astro-rss/src/pages/$episodes.astro2
-rw-r--r--www/src/components/Author.astro6
-rw-r--r--www/src/components/BaseHead.astro9
-rw-r--r--www/src/components/BlockQuote.astro9
-rw-r--r--www/src/components/BlogPost.astro12
-rw-r--r--www/src/components/BlogPostPreview.astro10
-rw-r--r--www/src/components/Note.astro7
-rw-r--r--www/src/components/Shell.astro5
-rw-r--r--www/src/layouts/Blog.astro2
57 files changed, 275 insertions, 132 deletions
diff --git a/.changeset/rich-starfishes-begin.md b/.changeset/rich-starfishes-begin.md
new file mode 100644
index 000000000..ad9edb09a
--- /dev/null
+++ b/.changeset/rich-starfishes-begin.md
@@ -0,0 +1,63 @@
+---
+'astro': minor
+---
+
+**This is a breaking change!**
+
+Astro props are now accessed from the `Astro.props` global. This change is meant to make prop definitions more ergonomic, leaning into JavaScript patterns you already know (destructuring and defaults). Astro components previously used a prop syntax borrowed from [Svelte](https://svelte.dev/docs#1_export_creates_a_component_prop), but it became clear that this was pretty confusing for most users.
+
+
+```diff
+ ---
++ const { text = 'Hello world!' } = Astro.props;
+- export let text = 'Hello world!';
+ ---
+
+ <div>{text}</div>
+```
+
+[Read more about the `.astro` syntax](https://github.com/snowpackjs/astro/blob/main/docs/syntax.md#data-and-props)
+
+---
+
+### How do I define what props my component accepts?
+
+Astro frontmatter scripts are TypeScript! Because of this, we can leverage TypeScript types to define the shape of your props.
+
+```ts
+---
+export interface Props {
+ text?: string;
+}
+const { text = 'Hello world!' } = Astro.props as Props;
+---
+```
+
+> **Note** Casting `Astro.props as Props` is a temporary workaround. We expect our Language Server to handle this automatically soon!
+
+### How do I access props I haven't explicitly defined?
+
+One of the great things about this change is that it's straight-forward to access _any_ props. Just use `...props`!
+
+```ts
+---
+export interface Props {
+ text?: string;
+ [attr: string]: unknown;
+}
+const { text = 'Hello world!', ...props } = Astro.props as Props;
+---
+```
+
+### What about prop validation?
+
+We considered building prop validation into Astro, but decided to leave that implementation up to you! This way, you can use any set of tools you like.
+
+```ts
+---
+const { text = 'Hello world!' } = Astro.props;
+
+if (typeof text !== 'string') throw new Error(`Expected "text" to be of type "string" but recieved "${typeof string}"!`);
+---
+```
+
diff --git a/docs-www/src/components/Note.astro b/docs-www/src/components/Note.astro
index 46940ddf8..c57ede3a0 100644
--- a/docs-www/src/components/Note.astro
+++ b/docs-www/src/components/Note.astro
@@ -1,6 +1,9 @@
---
-export let type = "tip";
-export let title;
+export interface Props {
+ title: string;
+ type?: 'tip' | 'warning' | 'error'
+}
+const { type = 'tip', title } = Astro.props;
---
<aside class={`note type-${type}`}>
diff --git a/docs-www/src/layouts/Main.astro b/docs-www/src/layouts/Main.astro
index 77407918a..0f1e6efe4 100644
--- a/docs-www/src/layouts/Main.astro
+++ b/docs-www/src/layouts/Main.astro
@@ -4,7 +4,7 @@ import SiteSidebar from '../components/SiteSidebar.astro';
import ThemeToggle from '../components/ThemeToggle.tsx';
import DocSidebar from '../components/DocSidebar.tsx';
-export let content;
+const { content } = Astro.props;
const headers = content?.astro?.headers;
let editHref = Astro?.request?.url?.pathname?.slice(1) ?? '';
if (editHref === '') editHref = `index`;
diff --git a/docs/api.md b/docs/api.md
index 05b722747..2f640b011 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -63,7 +63,7 @@ const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of po
### `collection`
```jsx
-export let collection;
+const { collection } = Astro.props;
```
When using the [Collections API][docs-collections], `collection` is a prop exposed to the page with the following shape:
diff --git a/docs/collections.md b/docs/collections.md
index 474bbe7a1..ee905b4db 100644
--- a/docs/collections.md
+++ b/docs/collections.md
@@ -23,7 +23,7 @@ To create a new Astro Collection, you must do three things:
2. Define and export the `collection` prop: `collection.data` is how you'll access the data for every page in the collection. Astro populates this prop for you automatically. It MUST be named `collection` and it must be exported.
-- Example: `export let collection;`
+- Example: `const { collection } = Astro.props;`
3. Define and export `createCollection` function: this tells Astro how to load and structure your collection data. Check out the examples below for documentation on how it should be implemented. It MUST be named `createCollection` and it must be exported.
@@ -35,7 +35,7 @@ To create a new Astro Collection, you must do three things:
```jsx
---
// Define the `collection` prop.
-export let collection: any;
+const { collection } = Astro.props;
// Define a `createCollection` function.
export async function createCollection() {
@@ -72,7 +72,7 @@ export async function createCollection() {
// prop also provides some important metadata for you to use, like: `collection.page`,
// `collection.url`, `collection.start`, `collection.end`, and `collection.total`.
// In this example, we'll use these values to do pagination in the template.
-export let collection: any;
+const { collection } = Astro.props;
export async function createCollection() { /* See Previous Example */ }
---
<html lang="en">
@@ -107,7 +107,7 @@ export async function createCollection() { /* See Previous Example */ }
```jsx
---
// Define the `collection` prop.
-export let collection: any;
+const { collection } = Astro.props;
// Define a `createCollection` function.
// In this example, we'll customize the URLs that we generate to
@@ -155,7 +155,7 @@ export async function createCollection() {
```jsx
---
// Define the `collection` prop.
-export let collection: any;
+const { collection } = Astro.props;
// Define a `createCollection` function.
// In this example, we'll create a new page for every single pokemon.
diff --git a/docs/markdown.md b/docs/markdown.md
index 822efb1ef..0a0d6bdb3 100644
--- a/docs/markdown.md
+++ b/docs/markdown.md
@@ -34,7 +34,7 @@ The rendered Markdown content is placed into the default `<slot />` element.
```jsx
---
-export let content;
+const { content } = Astro.props;
---
<html>
diff --git a/docs/styling.md b/docs/styling.md
index 6c4f10d44..b1438ad22 100644
--- a/docs/styling.md
+++ b/docs/styling.md
@@ -209,7 +209,7 @@ Instead, let `<Button>` control its own styles, and try a prop:
```jsx
---
// src/components/Button.astro
-export let theme;
+const { theme } = Astro.props;
---
<style lang="scss">
.btn {
diff --git a/docs/syntax.md b/docs/syntax.md
index 62b73104c..8476a2823 100644
--- a/docs/syntax.md
+++ b/docs/syntax.md
@@ -86,14 +86,27 @@ let name = 'world';
</main>
```
-`.astro` components can also accept props when they are rendered. Public props can be marked using the `export` keyword.
-
-Local values are overwritten when props are passed, otherwise they are considered the default value.
+`.astro` components can also accept props when they are rendered. Public props are exposed on the `Astro.props` global.
```jsx
---
-export let greeting = 'Hello';
-export let name;
+const { greeting = 'Hello', name } = Astro.props;
+---
+
+<main>
+ <h1>{greeting} {name}!</h1>
+</main>
+```
+
+To define the props which your component accepts, you may export a TypeScript interface or type named `Props`.
+```tsx
+---
+export interface Props {
+ name: string;
+ greeting?: string;
+}
+
+const { greeting = 'Hello', name } = Astro.props;
---
<main>
diff --git a/examples/astro-markdown/src/layouts/main.astro b/examples/astro-markdown/src/layouts/main.astro
index 61326cc5c..26993bcaf 100644
--- a/examples/astro-markdown/src/layouts/main.astro
+++ b/examples/astro-markdown/src/layouts/main.astro
@@ -1,5 +1,4 @@
---
-export let content;
---
<html>
diff --git a/examples/blog/src/components/MainHead.astro b/examples/blog/src/components/MainHead.astro
index dfc47fc01..fbdaa2965 100644
--- a/examples/blog/src/components/MainHead.astro
+++ b/examples/blog/src/components/MainHead.astro
@@ -1,12 +1,15 @@
---
-// props
-export let title: string;
-export let description: string;
-export let image: string | undefined;
-export let type: string | undefined;
-export let next: string | undefined;
-export let prev: string | undefined;
-export let canonicalURL: string | undefined;
+export interface Props {
+ title: string;
+ description: string;
+ image?: string;
+ type?: string;
+ next?: string;
+ prev?: string;
+ canonicalURL?: string;
+}
+
+const { title, description, image, type, next, prev, canonicalURL } = Astro.props as Props;
---
<!-- Common -->
diff --git a/examples/blog/src/components/Nav.astro b/examples/blog/src/components/Nav.astro
index 5c435b737..a7ef0985f 100644
--- a/examples/blog/src/components/Nav.astro
+++ b/examples/blog/src/components/Nav.astro
@@ -1,5 +1,8 @@
---
-export let title;
+export interface Props {
+ title: string;
+}
+const { title } = Astro.props;
---
<style lang="scss">
@@ -57,4 +60,4 @@ a {
<li><a href="/author/sancho">Author: Sancho</a></li>
<li><a href="/about">About</a></li>
</ul>
-</nav> \ No newline at end of file
+</nav>
diff --git a/examples/blog/src/components/Pagination.astro b/examples/blog/src/components/Pagination.astro
index 0294e1707..401931c07 100644
--- a/examples/blog/src/components/Pagination.astro
+++ b/examples/blog/src/components/Pagination.astro
@@ -1,6 +1,10 @@
---
-export let prevUrl: string;
-export let nextUrl: string;
+export interface Props {
+ prevUrl: string;
+ nextUrl: string;
+}
+
+const { prevUrl, nextUrl } = Astro.props;
---
<style lang="scss">
diff --git a/examples/blog/src/components/PostPreview.astro b/examples/blog/src/components/PostPreview.astro
index d02a23fe6..b126ca2fb 100644
--- a/examples/blog/src/components/PostPreview.astro
+++ b/examples/blog/src/components/PostPreview.astro
@@ -1,6 +1,9 @@
---
-export let post;
-export let author;
+export interface Props {
+ post: any;
+ author: string;
+}
+const { post, author } = Astro.props;
function formatDate(date) {
return new Date(date).toUTCString().replace(/(\d\d\d\d) .*/, '$1'); // remove everything after YYYY
diff --git a/examples/blog/src/layouts/post.astro b/examples/blog/src/layouts/post.astro
index 3379bd2dc..c8f394892 100644
--- a/examples/blog/src/layouts/post.astro
+++ b/examples/blog/src/layouts/post.astro
@@ -1,10 +1,9 @@
---
import MainHead from '../components/MainHead.astro';
import Nav from '../components/Nav.astro';
-
-export let content;
-
import authorData from '../data/authors.json';
+
+const { content } = Astro.props;
---
<html>
diff --git a/examples/blog/src/pages/$author.astro b/examples/blog/src/pages/$author.astro
index 687cabb42..e06d8bc94 100644
--- a/examples/blog/src/pages/$author.astro
+++ b/examples/blog/src/pages/$author.astro
@@ -12,7 +12,8 @@ const author = authorData[collection.params.author];
// collection
import authorData from '../data/authors.json';
-export let collection: any;
+
+let { collection } = Astro.props;
export async function createCollection() {
/** Load posts */
let allPosts = Astro.fetchContent('./post/*.md');
diff --git a/examples/blog/src/pages/$posts.astro b/examples/blog/src/pages/$posts.astro
index 688ec734d..0975e8007 100644
--- a/examples/blog/src/pages/$posts.astro
+++ b/examples/blog/src/pages/$posts.astro
@@ -11,7 +11,7 @@ let canonicalURL = Astro.request.canonicalURL;
// collection
import authorData from '../data/authors.json';
-export let collection: any;
+let { collection } = Astro.props;
export async function createCollection() {
return {
/** Load posts, sort newest -> oldest */
diff --git a/examples/docs/src/components/Note.astro b/examples/docs/src/components/Note.astro
index 46940ddf8..c57ede3a0 100644
--- a/examples/docs/src/components/Note.astro
+++ b/examples/docs/src/components/Note.astro
@@ -1,6 +1,9 @@
---
-export let type = "tip";
-export let title;
+export interface Props {
+ title: string;
+ type?: 'tip' | 'warning' | 'error'
+}
+const { type = 'tip', title } = Astro.props;
---
<aside class={`note type-${type}`}>
diff --git a/examples/docs/src/layouts/Main.astro b/examples/docs/src/layouts/Main.astro
index 77407918a..0f1e6efe4 100644
--- a/examples/docs/src/layouts/Main.astro
+++ b/examples/docs/src/layouts/Main.astro
@@ -4,7 +4,7 @@ import SiteSidebar from '../components/SiteSidebar.astro';
import ThemeToggle from '../components/ThemeToggle.tsx';
import DocSidebar from '../components/DocSidebar.tsx';
-export let content;
+const { content } = Astro.props;
const headers = content?.astro?.headers;
let editHref = Astro?.request?.url?.pathname?.slice(1) ?? '';
if (editHref === '') editHref = `index`;
diff --git a/examples/portfolio/src/components/MainHead.astro b/examples/portfolio/src/components/MainHead.astro
index c400b34e6..181da7d28 100644
--- a/examples/portfolio/src/components/MainHead.astro
+++ b/examples/portfolio/src/components/MainHead.astro
@@ -1,5 +1,5 @@
---
-export let title = 'Jeanine White: Personal Site';
+const { title = 'Jeanine White: Personal Site' } = Astro.props;
---
<meta charset="UTF-8">
diff --git a/examples/portfolio/src/layouts/project.astro b/examples/portfolio/src/layouts/project.astro
index 7ca9734a4..4ebfed8e1 100644
--- a/examples/portfolio/src/layouts/project.astro
+++ b/examples/portfolio/src/layouts/project.astro
@@ -4,7 +4,7 @@ import Button from '../components/Button/index.jsx';
import Footer from '../components/Footer/index.jsx';
import Nav from '../components/Nav/index.jsx';
-export let content: any;
+const { content } = Astro.props;
---
<html>
<head>
diff --git a/examples/portfolio/src/pages/$projects.astro b/examples/portfolio/src/pages/$projects.astro
index 5fd00d3c2..9a3407b83 100644
--- a/examples/portfolio/src/pages/$projects.astro
+++ b/examples/portfolio/src/pages/$projects.astro
@@ -4,7 +4,7 @@ import Footer from '../components/Footer/index.jsx';
import Nav from '../components/Nav/index.jsx';
import PortfolioPreview from '../components/PortfolioPreview/index.jsx';
-export let collection;
+let { collection } = Astro.props;
export async function createCollection() {
return {
async data() {
diff --git a/examples/snowpack/src/components/BaseHead.astro b/examples/snowpack/src/components/BaseHead.astro
index b74c0fa25..f83992662 100644
--- a/examples/snowpack/src/components/BaseHead.astro
+++ b/examples/snowpack/src/components/BaseHead.astro
@@ -2,9 +2,12 @@
import Banner from './Banner.astro';
import Nav from './Nav.astro';
-export let title: string;
-export let description: string;
-export let permalink: string;
+export interface Props {
+ title: string;
+ description: string;
+ permalink: string;
+}
+const { title, description, permalink } = Astro.props as Props;
---
<meta charset="utf-8" />
diff --git a/examples/snowpack/src/components/Button.astro b/examples/snowpack/src/components/Button.astro
index 00e14f01b..15b722893 100644
--- a/examples/snowpack/src/components/Button.astro
+++ b/examples/snowpack/src/components/Button.astro
@@ -1,5 +1,5 @@
---
-export let style;
+const { style } = Astro.props;
---
<style lang="scss">
diff --git a/examples/snowpack/src/components/Nav.astro b/examples/snowpack/src/components/Nav.astro
index 5305eaa78..243566d31 100644
--- a/examples/snowpack/src/components/Nav.astro
+++ b/examples/snowpack/src/components/Nav.astro
@@ -1,5 +1,8 @@
---
-export let version: string = '3.1.2';
+export interface Props {
+ version: string;
+}
+const { version = '3.1.2' } = Astro.props as Props;
---
<style lang="scss">
diff --git a/examples/snowpack/src/components/PokemonLookup.astro b/examples/snowpack/src/components/PokemonLookup.astro
index b3866791e..deac184dd 100644
--- a/examples/snowpack/src/components/PokemonLookup.astro
+++ b/examples/snowpack/src/components/PokemonLookup.astro
@@ -1,5 +1,8 @@
---
-export let number: number;
+export interface Props {
+ number: number;
+}
+const { number } = Astro.props;
const pokemonDataReq = await fetch(`https://pokeapi.co/api/v2/pokemon/${number}`);
const pokemonData = await pokemonDataReq.json();
diff --git a/examples/snowpack/src/components/Subnav.astro b/examples/snowpack/src/components/Subnav.astro
index 1709235dc..39ccebdef 100644
--- a/examples/snowpack/src/components/Subnav.astro
+++ b/examples/snowpack/src/components/Subnav.astro
@@ -1,7 +1,10 @@
---
-export let title: string;
-export let inputPath: string;
-export let headers: string;
+export interface Props {
+ title: string;
+ inputPath: string;
+ headers: string;
+}
+const { title, inputPath, headers } = Astro.props;
---
<style lang="scss">
diff --git a/examples/snowpack/src/layouts/content-with-cover.astro b/examples/snowpack/src/layouts/content-with-cover.astro
index 91cf9df39..ea2ecaf0e 100644
--- a/examples/snowpack/src/layouts/content-with-cover.astro
+++ b/examples/snowpack/src/layouts/content-with-cover.astro
@@ -4,7 +4,7 @@ import Menu from '../components/Menu.astro';
import BaseHead from '../components/BaseHead.astro';
import BaseLayout from '../components/BaseLayout.astro';
-export let content: any;
+const { content } = Astro.props;
---
<!doctype html>
diff --git a/examples/snowpack/src/layouts/content.astro b/examples/snowpack/src/layouts/content.astro
index 8ab619ba5..6c728db9d 100644
--- a/examples/snowpack/src/layouts/content.astro
+++ b/examples/snowpack/src/layouts/content.astro
@@ -4,7 +4,7 @@ import Menu from '../components/Menu.astro';
import BaseHead from '../components/BaseHead.astro';
import BaseLayout from '../components/BaseLayout.astro';
-export let content: any;
+const { content } = Astro.props;
---
<!doctype html>
diff --git a/examples/snowpack/src/layouts/post.astro b/examples/snowpack/src/layouts/post.astro
index 20dd4a287..7b0b614fe 100644
--- a/examples/snowpack/src/layouts/post.astro
+++ b/examples/snowpack/src/layouts/post.astro
@@ -3,7 +3,7 @@ import BaseHead from '../components/BaseHead.astro';
import BaseLayout from '../components/BaseLayout.astro';
import { format as formatDate, parseISO } from 'date-fns';
-export let content: any;
+const { content } = Astro.props;
---
<!doctype html>
diff --git a/packages/astro/components/Markdown.astro b/packages/astro/components/Markdown.astro
index cb0f9c003..f2e4f3335 100644
--- a/packages/astro/components/Markdown.astro
+++ b/packages/astro/components/Markdown.astro
@@ -1,8 +1,7 @@
---
import { renderMarkdown } from '@astrojs/markdown-support';
-export let content: string;
-export let $scope: string;
+const { content, $scope } = Astro.props;
let html = null;
// This flow is only triggered if a user passes `<Markdown content={content} />`
diff --git a/packages/astro/components/Prism.astro b/packages/astro/components/Prism.astro
index 1d0703bba..9b1f99717 100644
--- a/packages/astro/components/Prism.astro
+++ b/packages/astro/components/Prism.astro
@@ -3,8 +3,7 @@ import Prism from 'prismjs';
import { addAstro } from '@astrojs/prism';
import loadLanguages from 'prismjs/components/index.js';
-export let lang;
-export let code;
+const { lang, code } = Astro.props;
const languageMap = new Map([
['ts', 'typescript']
@@ -43,4 +42,4 @@ if (grammar) {
let className = lang ? `language-${lang}` : '';
---
-<pre class={className}><code class={className}>{html}</code></pre> \ No newline at end of file
+<pre class={className}><code class={className}>{html}</code></pre>
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index 4e3d00f78..6fc0404a8 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -60,6 +60,7 @@ export interface JsxItem {
export interface TransformResult {
script: string;
imports: string[];
+ exports: string[];
html: string;
css?: string;
/** If this page exports a collection, the JS to be executed as a string */
diff --git a/packages/astro/src/compiler/codegen/index.ts b/packages/astro/src/compiler/codegen/index.ts
index 02f898564..6d4a96032 100644
--- a/packages/astro/src/compiler/codegen/index.ts
+++ b/packages/astro/src/compiler/codegen/index.ts
@@ -260,7 +260,8 @@ interface CodegenState {
markers: {
insideMarkdown: boolean | Record<string, any>;
};
- importExportStatements: Set<string>;
+ exportStatements: Set<string>;
+ importStatements: Set<string>;
}
/** Compile/prepare Astro frontmatter scripts */
@@ -268,7 +269,6 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
const componentImports: ImportDeclaration[] = [];
const componentProps: VariableDeclarator[] = [];
const componentExports: ExportNamedDeclaration[] = [];
-
const contentImports = new Map<string, { spec: string; declarator: string }>();
let script = '';
@@ -299,9 +299,10 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
while (--i >= 0) {
const node = body[i];
switch (node.type) {
+ // case 'ExportAllDeclaration':
+ // case 'ExportDefaultDeclaration':
case 'ExportNamedDeclaration': {
if (!node.declaration) break;
- // const replacement = extract_exports(node);
if (node.declaration.type === 'VariableDeclaration') {
// case 1: prop (export let title)
@@ -312,15 +313,13 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
} else {
componentProps.push(declaration);
}
- body.splice(i, 1);
} else if (node.declaration.type === 'FunctionDeclaration') {
// case 2: createCollection (export async function)
if (!node.declaration.id || node.declaration.id.name !== 'createCollection') break;
createCollection = module.content.substring(node.start || 0, node.end || 0);
-
- // remove node
- body.splice(i, 1);
}
+
+ body.splice(i, 1);
break;
}
case 'FunctionDeclaration': {
@@ -376,24 +375,23 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
});
}
const { start, end } = componentImport;
- state.importExportStatements.add(module.content.slice(start || undefined, end || undefined));
+ state.importStatements.add(module.content.slice(start || undefined, end || undefined));
}
+
+ // TODO: actually expose componentExports other than __layout and __content
for (const componentImport of componentExports) {
const { start, end } = componentImport;
- state.importExportStatements.add(module.content.slice(start || undefined, end || undefined));
+ state.exportStatements.add(module.content.slice(start || undefined, end || undefined));
}
if (componentProps.length > 0) {
- propsStatement = 'let {';
- for (const componentExport of componentProps) {
- propsStatement += `${(componentExport.id as Identifier).name}`;
- const { init } = componentExport;
- if (init) {
- propsStatement += `= ${babelGenerator(init).code}`;
- }
- propsStatement += `,`;
- }
- propsStatement += `} = props;\n`;
+ const shortname = path.posix.relative(compileOptions.astroConfig.projectRoot.pathname, state.filename);
+ const props = componentProps.map(prop => (prop.id as Identifier)?.name).filter(v => v);
+ console.log();
+ warn(compileOptions.logging, shortname, yellow(`\nDefining props with "export" has been removed! Please see https://github.com/snowpackjs/astro/blob/main/packages/astro/CHANGELOG.md#0150
+Please update your code to use:
+
+const { ${props.join(', ')} } = Astro.props;\n`));
}
// handle createCollection, if any
@@ -448,12 +446,16 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
for (const [namespace, { spec }] of contentImports.entries()) {
const globResult = fetchContent(spec, { namespace, filename: state.filename });
for (const importStatement of globResult.imports) {
- state.importExportStatements.add(importStatement);
+ state.importStatements.add(importStatement);
}
contentCode += globResult.code;
}
script = propsStatement + contentCode + babelGenerator(program).code;
+ const location = { start: module.start, end: module.end };
+ let transpiledScript = compileExpressionSafe(script, { state, compileOptions, location });
+ if (transpiledScript === null) throw new Error(`Unable to compile script`);
+ script = transpiledScript;
}
return {
@@ -491,7 +493,7 @@ const FALSY_EXPRESSIONS = new Set(['false', 'null', 'undefined', 'void 0']);
/** Compile page markup */
async function compileHtml(enterNode: TemplateNode, state: CodegenState, compileOptions: CompileOptions): Promise<string> {
return new Promise((resolve) => {
- const { components, css, importExportStatements, filename, fileID } = state;
+ const { components, css, importStatements, exportStatements, filename, fileID } = state;
const { astroConfig } = compileOptions;
let paren = -1;
@@ -570,8 +572,8 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
case 'InlineComponent': {
switch (node.name) {
case 'Prism': {
- if (!importExportStatements.has(PRISM_IMPORT)) {
- importExportStatements.add(PRISM_IMPORT);
+ if (!importStatements.has(PRISM_IMPORT)) {
+ importStatements.add(PRISM_IMPORT);
}
if (!components.has('Prism')) {
components.set('Prism', {
@@ -634,7 +636,7 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
}
const { wrapper, wrapperImport } = getComponentWrapper(name, componentInfo, { astroConfig, filename });
if (wrapperImport) {
- importExportStatements.add(wrapperImport);
+ importStatements.add(wrapperImport);
}
if (curr === 'markdown') {
await pushMarkdownToBuffer();
@@ -777,7 +779,8 @@ export async function codegen(ast: Ast, { compileOptions, filename, fileID }: Co
markers: {
insideMarkdown: false,
},
- importExportStatements: new Set(),
+ importStatements: new Set(),
+ exportStatements: new Set(),
};
const { script, createCollection } = compileModule(ast.module, state, compileOptions);
@@ -788,7 +791,8 @@ export async function codegen(ast: Ast, { compileOptions, filename, fileID }: Co
return {
script: script,
- imports: Array.from(state.importExportStatements),
+ imports: Array.from(state.importStatements),
+ exports: Array.from(state.exportStatements),
html,
css: state.css.length ? state.css.join('\n\n') : undefined,
createCollection,
diff --git a/packages/astro/src/compiler/index.ts b/packages/astro/src/compiler/index.ts
index aad0be330..4a3b359ce 100644
--- a/packages/astro/src/compiler/index.ts
+++ b/packages/astro/src/compiler/index.ts
@@ -105,6 +105,7 @@ interface CompileComponentOptions {
projectRoot: string;
isPage?: boolean;
}
+/** Compiles an Astro component */
export async function compileComponent(source: string, { compileOptions, filename, projectRoot, isPage }: CompileComponentOptions): Promise<CompileResult> {
const result = await transformFromSource(source, { compileOptions, filename, projectRoot });
const site = compileOptions.astroConfig.buildOptions.site || `http://localhost:${compileOptions.astroConfig.devOptions.port}`;
@@ -121,9 +122,10 @@ import { h, Fragment } from 'astro/dist/internal/h.js';
const __astroInternal = Symbol('astro.internal');
async function __render(props, ...children) {
const Astro = {
+ props,
+ site: new URL('/', ${JSON.stringify(site)}),
css: props[__astroInternal]?.css || [],
request: props[__astroInternal]?.request || {},
- site: new URL('/', ${JSON.stringify(site)}),
isPage: props[__astroInternal]?.isPage || false
};
@@ -144,11 +146,15 @@ export async function __renderPage({request, children, props, css}) {
__render,
};
- props[__astroInternal] = {
- request,
- css,
- isPage: true
- };
+ Object.defineProperty(props, __astroInternal, {
+ value: {
+ request,
+ css,
+ isPage: true
+ },
+ writable: false,
+ enumerable: false
+ })
const childBodyResult = await currentChild.__render(props, children);
@@ -162,7 +168,11 @@ export async function __renderPage({request, children, props, css}) {
}
return childBodyResult;
-};\n`;
+};
+
+${result.exports.join('\n')}
+
+`;
return {
result,
diff --git a/packages/astro/src/runtime.ts b/packages/astro/src/runtime.ts
index e859a60d5..fe7890e28 100644
--- a/packages/astro/src/runtime.ts
+++ b/packages/astro/src/runtime.ts
@@ -228,7 +228,7 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
canonicalURL: canonicalURL(requestURL.pathname, requestURL.origin),
},
children: [],
- props: { collection },
+ props: Object.keys(collection).length > 0 ? { collection } : {},
css: Array.isArray(mod.css) ? mod.css : typeof mod.css === 'string' ? [mod.css] : [],
})) as string;
diff --git a/packages/astro/test/fixtures/astro-basic/src/layouts/base.astro b/packages/astro/test/fixtures/astro-basic/src/layouts/base.astro
index ec996a32f..12f597ff1 100644
--- a/packages/astro/test/fixtures/astro-basic/src/layouts/base.astro
+++ b/packages/astro/test/fixtures/astro-basic/src/layouts/base.astro
@@ -1,5 +1,5 @@
---
-export let content: any;
+const { content } = Astro.props;
---
<!doctype html>
@@ -14,4 +14,4 @@ export let content: any;
<main><slot></slot></main>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/packages/astro/test/fixtures/astro-collection/src/pages/$grouped.astro b/packages/astro/test/fixtures/astro-collection/src/pages/$grouped.astro
index 0bcae6b6b..61be75629 100644
--- a/packages/astro/test/fixtures/astro-collection/src/pages/$grouped.astro
+++ b/packages/astro/test/fixtures/astro-collection/src/pages/$grouped.astro
@@ -1,5 +1,5 @@
---
-export let collection: any;
+const { collection } = Astro.props;
export async function createCollection() {
const allPosts = Astro.fetchContent('./post/**/*.md');
@@ -27,4 +27,4 @@ export async function createCollection() {
{collection.data.map((post) => (
<a href={post.url}>{post.title}</a>
))}
-</div> \ No newline at end of file
+</div>
diff --git a/packages/astro/test/fixtures/astro-collection/src/pages/$individual.astro b/packages/astro/test/fixtures/astro-collection/src/pages/$individual.astro
index 5b7b7fda1..4df04b50f 100644
--- a/packages/astro/test/fixtures/astro-collection/src/pages/$individual.astro
+++ b/packages/astro/test/fixtures/astro-collection/src/pages/$individual.astro
@@ -1,5 +1,5 @@
---
-export let collection: any;
+const { collection } = Astro.props;
export async function createCollection() {
const allPosts = Astro.fetchContent('./post/*.md');
@@ -26,4 +26,4 @@ export async function createCollection() {
<div id={collection.params.slug}>
<h1>{collection.data[0].title}</h1>
-</div> \ No newline at end of file
+</div>
diff --git a/packages/astro/test/fixtures/astro-collection/src/pages/$nested.astro b/packages/astro/test/fixtures/astro-collection/src/pages/$nested.astro
index 9e87d3588..507871a8b 100644
--- a/packages/astro/test/fixtures/astro-collection/src/pages/$nested.astro
+++ b/packages/astro/test/fixtures/astro-collection/src/pages/$nested.astro
@@ -1,5 +1,5 @@
---
-export let collection: any;
+const { collection } = Astro.props;
export async function createCollection() {
return {
diff --git a/packages/astro/test/fixtures/astro-collection/src/pages/$paginated.astro b/packages/astro/test/fixtures/astro-collection/src/pages/$paginated.astro
index 5e83aab05..d7383dffe 100644
--- a/packages/astro/test/fixtures/astro-collection/src/pages/$paginated.astro
+++ b/packages/astro/test/fixtures/astro-collection/src/pages/$paginated.astro
@@ -1,5 +1,5 @@
---
-export let collection: any;
+const { collection } = Astro.props;
export async function createCollection() {
return {
diff --git a/packages/astro/test/fixtures/astro-collection/src/pages/$remote.astro b/packages/astro/test/fixtures/astro-collection/src/pages/$remote.astro
index 07ecbb82c..35df303c2 100644
--- a/packages/astro/test/fixtures/astro-collection/src/pages/$remote.astro
+++ b/packages/astro/test/fixtures/astro-collection/src/pages/$remote.astro
@@ -1,5 +1,5 @@
---
-export let collection: any;
+const { collection } = Astro.props;
export async function createCollection() {
const data = await Promise.all([
diff --git a/packages/astro/test/fixtures/astro-collection/src/pages/$shallow.astro b/packages/astro/test/fixtures/astro-collection/src/pages/$shallow.astro
index 01e30f398..d937b3326 100644
--- a/packages/astro/test/fixtures/astro-collection/src/pages/$shallow.astro
+++ b/packages/astro/test/fixtures/astro-collection/src/pages/$shallow.astro
@@ -1,5 +1,5 @@
---
-export let collection: any;
+const { collection } = Astro.props;
export async function createCollection() {
return {
diff --git a/packages/astro/test/fixtures/astro-global/src/layouts/post.astro b/packages/astro/test/fixtures/astro-global/src/layouts/post.astro
index 4d75a3c28..87e5cc448 100644
--- a/packages/astro/test/fixtures/astro-global/src/layouts/post.astro
+++ b/packages/astro/test/fixtures/astro-global/src/layouts/post.astro
@@ -1,5 +1,5 @@
---
-export let content;
+const { content } = Astro.props;
---
<html>
<head>
diff --git a/packages/astro/test/fixtures/astro-global/src/pages/$posts.astro b/packages/astro/test/fixtures/astro-global/src/pages/$posts.astro
index 2384f6ba9..cb07361fb 100644
--- a/packages/astro/test/fixtures/astro-global/src/pages/$posts.astro
+++ b/packages/astro/test/fixtures/astro-global/src/pages/$posts.astro
@@ -1,5 +1,5 @@
---
-export let collection;
+const { collection } = Astro.props;
export function createCollection() {
return {
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/code.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/code.astro
index 19aa90e18..a4671a34a 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/code.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/code.astro
@@ -1,7 +1,7 @@
---
import { Markdown } from 'astro/components';
-export const title = 'My Blog Post';
-export const description = 'This is a post about some stuff.';
+const title = 'My Blog Post';
+const description = 'This is a post about some stuff.';
---
<Markdown>
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/complex.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/complex.astro
index 4d470f48d..f65c60080 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/complex.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/complex.astro
@@ -4,8 +4,8 @@ import Layout from '../layouts/content.astro';
import Hello from '../components/Hello.jsx';
import Counter from '../components/Counter.jsx';
-export const title = 'My Blog Post';
-export const description = 'This is a post about some stuff.';
+const title = 'My Blog Post';
+const description = 'This is a post about some stuff.';
---
<Markdown>
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/deep.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/deep.astro
index b2a8b51a7..fa2a747cd 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/deep.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/deep.astro
@@ -4,8 +4,8 @@ import Layout from '../layouts/content.astro';
import Hello from '../components/Hello.jsx';
import Counter from '../components/Counter.jsx';
-export const title = 'My Blog Post';
-export const description = 'This is a post about some stuff.';
+const title = 'My Blog Post';
+const description = 'This is a post about some stuff.';
---
<div id="deep">
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/post.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/post.astro
index b8cffd8c4..20780d631 100644
--- a/packages/astro/test/fixtures/astro-markdown/src/pages/post.astro
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/post.astro
@@ -3,8 +3,8 @@ import { Markdown } from 'astro/components';
import Layout from '../layouts/content.astro';
import Example from '../components/Example.jsx';
-export const title = 'My Blog Post';
-export const description = 'This is a post about some stuff.';
+const title = 'My Blog Post';
+const description = 'This is a post about some stuff.';
---
<Markdown>
diff --git a/packages/astro/test/fixtures/astro-rss/src/pages/$episodes.astro b/packages/astro/test/fixtures/astro-rss/src/pages/$episodes.astro
index 686770480..d3e5aaecf 100644
--- a/packages/astro/test/fixtures/astro-rss/src/pages/$episodes.astro
+++ b/packages/astro/test/fixtures/astro-rss/src/pages/$episodes.astro
@@ -1,5 +1,5 @@
---
-export let collection;
+const { collection } = Astro.props;
export async function createCollection() {
return {
diff --git a/www/src/components/Author.astro b/www/src/components/Author.astro
index e11021ea9..56013ceb7 100644
--- a/www/src/components/Author.astro
+++ b/www/src/components/Author.astro
@@ -1,7 +1,11 @@
---
import authorData from '../data/authors.json';
-export let authorId: string;
+export interface Props {
+ authorId: string;
+}
+
+const { authorId } = Astro.props;
const author = authorData[authorId];
---
<style>
diff --git a/www/src/components/BaseHead.astro b/www/src/components/BaseHead.astro
index ee7510bde..4905e1b7a 100644
--- a/www/src/components/BaseHead.astro
+++ b/www/src/components/BaseHead.astro
@@ -1,7 +1,10 @@
---
-export let title: string;
-export let description: string;
-export let permalink: string;
+export interface Props {
+ title: string;
+ description: string;
+ permalink: string;
+}
+const { title, description, permalink } = Astro.props;
---
<meta charset="utf-8" />
diff --git a/www/src/components/BlockQuote.astro b/www/src/components/BlockQuote.astro
index 00d380ef1..f1f594461 100644
--- a/www/src/components/BlockQuote.astro
+++ b/www/src/components/BlockQuote.astro
@@ -1,7 +1,10 @@
---
-export let author: string;
-export let source: string;
-export let sourceHref: string;
+export interface Props {
+ author: string;
+ source: string;
+ sourceHref: string;
+}
+const { author, source, sourceHref } = Astro.props;
---
<blockquote>
diff --git a/www/src/components/BlogPost.astro b/www/src/components/BlogPost.astro
index 0884c6575..04d8e4324 100644
--- a/www/src/components/BlogPost.astro
+++ b/www/src/components/BlogPost.astro
@@ -2,10 +2,14 @@
import Author from './Author.astro';
import GithubStarButton from './GithubStarButton.astro';
-export let title: string;
-export let author: string;
-export let publishDate: string;
-export let heroImage: string;
+export interface Props {
+ title: string;
+ author: string;
+ publishDate: string;
+ heroImage: string;
+}
+
+const { title, author, publishDate, heroImage } = Astro.props;
---
<div class="layout">
diff --git a/www/src/components/BlogPostPreview.astro b/www/src/components/BlogPostPreview.astro
index c38a56361..bb44c4abb 100644
--- a/www/src/components/BlogPostPreview.astro
+++ b/www/src/components/BlogPostPreview.astro
@@ -1,9 +1,13 @@
---
import Author from './Author.astro';
-export let title: string;
-export let publishDate: string;
-export let href: string;
+export interface Props {
+ title: string;
+ publishDate: string;
+ href: string;
+}
+
+const { title, publishDate, href } = Astro.props;
---
<article class="post-preview">
<header>
diff --git a/www/src/components/Note.astro b/www/src/components/Note.astro
index 3618e3020..12fc396bc 100644
--- a/www/src/components/Note.astro
+++ b/www/src/components/Note.astro
@@ -1,6 +1,9 @@
---
-export let type = "tip";
-export let title;
+export interface Props {
+ title: string;
+ type?: 'tip' | 'warning' | 'error'
+}
+const { type = 'tip', title } = Astro.props;
---
<aside class={`note type-${type}`}>
diff --git a/www/src/components/Shell.astro b/www/src/components/Shell.astro
index 77753de4b..d2738329a 100644
--- a/www/src/components/Shell.astro
+++ b/www/src/components/Shell.astro
@@ -1,5 +1,8 @@
---
-export let code: string;
+export interface Props {
+ code: string;
+}
+const { code } = Astro.props;
---
<pre><code>{code.trim().split('\n').map(ln => <span class="line">
diff --git a/www/src/layouts/Blog.astro b/www/src/layouts/Blog.astro
index 2bca03b7e..d1737bfb7 100644
--- a/www/src/layouts/Blog.astro
+++ b/www/src/layouts/Blog.astro
@@ -4,7 +4,7 @@ import SiteSidebar from '../components/SiteSidebar.astro';
import ThemeToggle from '../components/ThemeToggle.tsx';
import DocSidebar from '../components/DocSidebar.tsx';
-export let content;
+const { content } = Astro.props;
const headers = content?.astro?.headers;
---