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
This commit is contained in:
parent
bc9e0f180c
commit
a136c85e6b
57 changed files with 275 additions and 132 deletions
63
.changeset/rich-starfishes-begin.md
Normal file
63
.changeset/rich-starfishes-begin.md
Normal file
|
@ -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}"!`);
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
---
|
---
|
||||||
export let type = "tip";
|
export interface Props {
|
||||||
export let title;
|
title: string;
|
||||||
|
type?: 'tip' | 'warning' | 'error'
|
||||||
|
}
|
||||||
|
const { type = 'tip', title } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<aside class={`note type-${type}`}>
|
<aside class={`note type-${type}`}>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import SiteSidebar from '../components/SiteSidebar.astro';
|
||||||
import ThemeToggle from '../components/ThemeToggle.tsx';
|
import ThemeToggle from '../components/ThemeToggle.tsx';
|
||||||
import DocSidebar from '../components/DocSidebar.tsx';
|
import DocSidebar from '../components/DocSidebar.tsx';
|
||||||
|
|
||||||
export let content;
|
const { content } = Astro.props;
|
||||||
const headers = content?.astro?.headers;
|
const headers = content?.astro?.headers;
|
||||||
let editHref = Astro?.request?.url?.pathname?.slice(1) ?? '';
|
let editHref = Astro?.request?.url?.pathname?.slice(1) ?? '';
|
||||||
if (editHref === '') editHref = `index`;
|
if (editHref === '') editHref = `index`;
|
||||||
|
|
|
@ -63,7 +63,7 @@ const data = Astro.fetchContent('../pages/post/*.md'); // returns an array of po
|
||||||
### `collection`
|
### `collection`
|
||||||
|
|
||||||
```jsx
|
```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:
|
When using the [Collections API][docs-collections], `collection` is a prop exposed to the page with the following shape:
|
||||||
|
|
|
@ -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.
|
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.
|
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
|
```jsx
|
||||||
---
|
---
|
||||||
// Define the `collection` prop.
|
// Define the `collection` prop.
|
||||||
export let collection: any;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
// Define a `createCollection` function.
|
// Define a `createCollection` function.
|
||||||
export async function createCollection() {
|
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`,
|
// prop also provides some important metadata for you to use, like: `collection.page`,
|
||||||
// `collection.url`, `collection.start`, `collection.end`, and `collection.total`.
|
// `collection.url`, `collection.start`, `collection.end`, and `collection.total`.
|
||||||
// In this example, we'll use these values to do pagination in the template.
|
// 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 */ }
|
export async function createCollection() { /* See Previous Example */ }
|
||||||
---
|
---
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
@ -107,7 +107,7 @@ export async function createCollection() { /* See Previous Example */ }
|
||||||
```jsx
|
```jsx
|
||||||
---
|
---
|
||||||
// Define the `collection` prop.
|
// Define the `collection` prop.
|
||||||
export let collection: any;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
// Define a `createCollection` function.
|
// Define a `createCollection` function.
|
||||||
// In this example, we'll customize the URLs that we generate to
|
// In this example, we'll customize the URLs that we generate to
|
||||||
|
@ -155,7 +155,7 @@ export async function createCollection() {
|
||||||
```jsx
|
```jsx
|
||||||
---
|
---
|
||||||
// Define the `collection` prop.
|
// Define the `collection` prop.
|
||||||
export let collection: any;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
// Define a `createCollection` function.
|
// Define a `createCollection` function.
|
||||||
// In this example, we'll create a new page for every single pokemon.
|
// In this example, we'll create a new page for every single pokemon.
|
||||||
|
|
|
@ -34,7 +34,7 @@ The rendered Markdown content is placed into the default `<slot />` element.
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
---
|
---
|
||||||
export let content;
|
const { content } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
|
|
|
@ -209,7 +209,7 @@ Instead, let `<Button>` control its own styles, and try a prop:
|
||||||
```jsx
|
```jsx
|
||||||
---
|
---
|
||||||
// src/components/Button.astro
|
// src/components/Button.astro
|
||||||
export let theme;
|
const { theme } = Astro.props;
|
||||||
---
|
---
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.btn {
|
.btn {
|
||||||
|
|
|
@ -86,14 +86,27 @@ let name = 'world';
|
||||||
</main>
|
</main>
|
||||||
```
|
```
|
||||||
|
|
||||||
`.astro` components can also accept props when they are rendered. Public props can be marked using the `export` keyword.
|
`.astro` components can also accept props when they are rendered. Public props are exposed on the `Astro.props` global.
|
||||||
|
|
||||||
Local values are overwritten when props are passed, otherwise they are considered the default value.
|
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
---
|
---
|
||||||
export let greeting = 'Hello';
|
const { greeting = 'Hello', name } = Astro.props;
|
||||||
export let name;
|
---
|
||||||
|
|
||||||
|
<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>
|
<main>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
---
|
---
|
||||||
export let content;
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
---
|
---
|
||||||
// props
|
export interface Props {
|
||||||
export let title: string;
|
title: string;
|
||||||
export let description: string;
|
description: string;
|
||||||
export let image: string | undefined;
|
image?: string;
|
||||||
export let type: string | undefined;
|
type?: string;
|
||||||
export let next: string | undefined;
|
next?: string;
|
||||||
export let prev: string | undefined;
|
prev?: string;
|
||||||
export let canonicalURL: string | undefined;
|
canonicalURL?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { title, description, image, type, next, prev, canonicalURL } = Astro.props as Props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- Common -->
|
<!-- Common -->
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
---
|
---
|
||||||
export let title;
|
export interface Props {
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
const { title } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -57,4 +60,4 @@ a {
|
||||||
<li><a href="/author/sancho">Author: Sancho</a></li>
|
<li><a href="/author/sancho">Author: Sancho</a></li>
|
||||||
<li><a href="/about">About</a></li>
|
<li><a href="/about">About</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
---
|
---
|
||||||
export let prevUrl: string;
|
export interface Props {
|
||||||
export let nextUrl: string;
|
prevUrl: string;
|
||||||
|
nextUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { prevUrl, nextUrl } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
---
|
---
|
||||||
export let post;
|
export interface Props {
|
||||||
export let author;
|
post: any;
|
||||||
|
author: string;
|
||||||
|
}
|
||||||
|
const { post, author } = Astro.props;
|
||||||
|
|
||||||
function formatDate(date) {
|
function formatDate(date) {
|
||||||
return new Date(date).toUTCString().replace(/(\d\d\d\d) .*/, '$1'); // remove everything after YYYY
|
return new Date(date).toUTCString().replace(/(\d\d\d\d) .*/, '$1'); // remove everything after YYYY
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
---
|
---
|
||||||
import MainHead from '../components/MainHead.astro';
|
import MainHead from '../components/MainHead.astro';
|
||||||
import Nav from '../components/Nav.astro';
|
import Nav from '../components/Nav.astro';
|
||||||
|
|
||||||
export let content;
|
|
||||||
|
|
||||||
import authorData from '../data/authors.json';
|
import authorData from '../data/authors.json';
|
||||||
|
|
||||||
|
const { content } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
|
|
|
@ -12,7 +12,8 @@ const author = authorData[collection.params.author];
|
||||||
|
|
||||||
// collection
|
// collection
|
||||||
import authorData from '../data/authors.json';
|
import authorData from '../data/authors.json';
|
||||||
export let collection: any;
|
|
||||||
|
let { collection } = Astro.props;
|
||||||
export async function createCollection() {
|
export async function createCollection() {
|
||||||
/** Load posts */
|
/** Load posts */
|
||||||
let allPosts = Astro.fetchContent('./post/*.md');
|
let allPosts = Astro.fetchContent('./post/*.md');
|
||||||
|
|
|
@ -11,7 +11,7 @@ let canonicalURL = Astro.request.canonicalURL;
|
||||||
|
|
||||||
// collection
|
// collection
|
||||||
import authorData from '../data/authors.json';
|
import authorData from '../data/authors.json';
|
||||||
export let collection: any;
|
let { collection } = Astro.props;
|
||||||
export async function createCollection() {
|
export async function createCollection() {
|
||||||
return {
|
return {
|
||||||
/** Load posts, sort newest -> oldest */
|
/** Load posts, sort newest -> oldest */
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
---
|
---
|
||||||
export let type = "tip";
|
export interface Props {
|
||||||
export let title;
|
title: string;
|
||||||
|
type?: 'tip' | 'warning' | 'error'
|
||||||
|
}
|
||||||
|
const { type = 'tip', title } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<aside class={`note type-${type}`}>
|
<aside class={`note type-${type}`}>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import SiteSidebar from '../components/SiteSidebar.astro';
|
||||||
import ThemeToggle from '../components/ThemeToggle.tsx';
|
import ThemeToggle from '../components/ThemeToggle.tsx';
|
||||||
import DocSidebar from '../components/DocSidebar.tsx';
|
import DocSidebar from '../components/DocSidebar.tsx';
|
||||||
|
|
||||||
export let content;
|
const { content } = Astro.props;
|
||||||
const headers = content?.astro?.headers;
|
const headers = content?.astro?.headers;
|
||||||
let editHref = Astro?.request?.url?.pathname?.slice(1) ?? '';
|
let editHref = Astro?.request?.url?.pathname?.slice(1) ?? '';
|
||||||
if (editHref === '') editHref = `index`;
|
if (editHref === '') editHref = `index`;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let title = 'Jeanine White: Personal Site';
|
const { title = 'Jeanine White: Personal Site' } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Button from '../components/Button/index.jsx';
|
||||||
import Footer from '../components/Footer/index.jsx';
|
import Footer from '../components/Footer/index.jsx';
|
||||||
import Nav from '../components/Nav/index.jsx';
|
import Nav from '../components/Nav/index.jsx';
|
||||||
|
|
||||||
export let content: any;
|
const { content } = Astro.props;
|
||||||
---
|
---
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Footer from '../components/Footer/index.jsx';
|
||||||
import Nav from '../components/Nav/index.jsx';
|
import Nav from '../components/Nav/index.jsx';
|
||||||
import PortfolioPreview from '../components/PortfolioPreview/index.jsx';
|
import PortfolioPreview from '../components/PortfolioPreview/index.jsx';
|
||||||
|
|
||||||
export let collection;
|
let { collection } = Astro.props;
|
||||||
export async function createCollection() {
|
export async function createCollection() {
|
||||||
return {
|
return {
|
||||||
async data() {
|
async data() {
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
import Banner from './Banner.astro';
|
import Banner from './Banner.astro';
|
||||||
import Nav from './Nav.astro';
|
import Nav from './Nav.astro';
|
||||||
|
|
||||||
export let title: string;
|
export interface Props {
|
||||||
export let description: string;
|
title: string;
|
||||||
export let permalink: string;
|
description: string;
|
||||||
|
permalink: string;
|
||||||
|
}
|
||||||
|
const { title, description, permalink } = Astro.props as Props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let style;
|
const { style } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
@ -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">
|
<style lang="scss">
|
||||||
|
|
|
@ -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 pokemonDataReq = await fetch(`https://pokeapi.co/api/v2/pokemon/${number}`);
|
||||||
const pokemonData = await pokemonDataReq.json();
|
const pokemonData = await pokemonDataReq.json();
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
---
|
---
|
||||||
export let title: string;
|
export interface Props {
|
||||||
export let inputPath: string;
|
title: string;
|
||||||
export let headers: string;
|
inputPath: string;
|
||||||
|
headers: string;
|
||||||
|
}
|
||||||
|
const { title, inputPath, headers } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Menu from '../components/Menu.astro';
|
||||||
import BaseHead from '../components/BaseHead.astro';
|
import BaseHead from '../components/BaseHead.astro';
|
||||||
import BaseLayout from '../components/BaseLayout.astro';
|
import BaseLayout from '../components/BaseLayout.astro';
|
||||||
|
|
||||||
export let content: any;
|
const { content } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Menu from '../components/Menu.astro';
|
||||||
import BaseHead from '../components/BaseHead.astro';
|
import BaseHead from '../components/BaseHead.astro';
|
||||||
import BaseLayout from '../components/BaseLayout.astro';
|
import BaseLayout from '../components/BaseLayout.astro';
|
||||||
|
|
||||||
export let content: any;
|
const { content } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import BaseHead from '../components/BaseHead.astro';
|
||||||
import BaseLayout from '../components/BaseLayout.astro';
|
import BaseLayout from '../components/BaseLayout.astro';
|
||||||
import { format as formatDate, parseISO } from 'date-fns';
|
import { format as formatDate, parseISO } from 'date-fns';
|
||||||
|
|
||||||
export let content: any;
|
const { content } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
---
|
---
|
||||||
import { renderMarkdown } from '@astrojs/markdown-support';
|
import { renderMarkdown } from '@astrojs/markdown-support';
|
||||||
|
|
||||||
export let content: string;
|
const { content, $scope } = Astro.props;
|
||||||
export let $scope: string;
|
|
||||||
let html = null;
|
let html = null;
|
||||||
|
|
||||||
// This flow is only triggered if a user passes `<Markdown content={content} />`
|
// This flow is only triggered if a user passes `<Markdown content={content} />`
|
||||||
|
|
|
@ -3,8 +3,7 @@ import Prism from 'prismjs';
|
||||||
import { addAstro } from '@astrojs/prism';
|
import { addAstro } from '@astrojs/prism';
|
||||||
import loadLanguages from 'prismjs/components/index.js';
|
import loadLanguages from 'prismjs/components/index.js';
|
||||||
|
|
||||||
export let lang;
|
const { lang, code } = Astro.props;
|
||||||
export let code;
|
|
||||||
|
|
||||||
const languageMap = new Map([
|
const languageMap = new Map([
|
||||||
['ts', 'typescript']
|
['ts', 'typescript']
|
||||||
|
@ -43,4 +42,4 @@ if (grammar) {
|
||||||
let className = lang ? `language-${lang}` : '';
|
let className = lang ? `language-${lang}` : '';
|
||||||
---
|
---
|
||||||
|
|
||||||
<pre class={className}><code class={className}>{html}</code></pre>
|
<pre class={className}><code class={className}>{html}</code></pre>
|
||||||
|
|
|
@ -60,6 +60,7 @@ export interface JsxItem {
|
||||||
export interface TransformResult {
|
export interface TransformResult {
|
||||||
script: string;
|
script: string;
|
||||||
imports: string[];
|
imports: string[];
|
||||||
|
exports: string[];
|
||||||
html: string;
|
html: string;
|
||||||
css?: string;
|
css?: string;
|
||||||
/** If this page exports a collection, the JS to be executed as a string */
|
/** If this page exports a collection, the JS to be executed as a string */
|
||||||
|
|
|
@ -260,7 +260,8 @@ interface CodegenState {
|
||||||
markers: {
|
markers: {
|
||||||
insideMarkdown: boolean | Record<string, any>;
|
insideMarkdown: boolean | Record<string, any>;
|
||||||
};
|
};
|
||||||
importExportStatements: Set<string>;
|
exportStatements: Set<string>;
|
||||||
|
importStatements: Set<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compile/prepare Astro frontmatter scripts */
|
/** Compile/prepare Astro frontmatter scripts */
|
||||||
|
@ -268,7 +269,6 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
|
||||||
const componentImports: ImportDeclaration[] = [];
|
const componentImports: ImportDeclaration[] = [];
|
||||||
const componentProps: VariableDeclarator[] = [];
|
const componentProps: VariableDeclarator[] = [];
|
||||||
const componentExports: ExportNamedDeclaration[] = [];
|
const componentExports: ExportNamedDeclaration[] = [];
|
||||||
|
|
||||||
const contentImports = new Map<string, { spec: string; declarator: string }>();
|
const contentImports = new Map<string, { spec: string; declarator: string }>();
|
||||||
|
|
||||||
let script = '';
|
let script = '';
|
||||||
|
@ -299,9 +299,10 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
|
||||||
while (--i >= 0) {
|
while (--i >= 0) {
|
||||||
const node = body[i];
|
const node = body[i];
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
|
// case 'ExportAllDeclaration':
|
||||||
|
// case 'ExportDefaultDeclaration':
|
||||||
case 'ExportNamedDeclaration': {
|
case 'ExportNamedDeclaration': {
|
||||||
if (!node.declaration) break;
|
if (!node.declaration) break;
|
||||||
// const replacement = extract_exports(node);
|
|
||||||
|
|
||||||
if (node.declaration.type === 'VariableDeclaration') {
|
if (node.declaration.type === 'VariableDeclaration') {
|
||||||
// case 1: prop (export let title)
|
// case 1: prop (export let title)
|
||||||
|
@ -312,15 +313,13 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
|
||||||
} else {
|
} else {
|
||||||
componentProps.push(declaration);
|
componentProps.push(declaration);
|
||||||
}
|
}
|
||||||
body.splice(i, 1);
|
|
||||||
} else if (node.declaration.type === 'FunctionDeclaration') {
|
} else if (node.declaration.type === 'FunctionDeclaration') {
|
||||||
// case 2: createCollection (export async function)
|
// case 2: createCollection (export async function)
|
||||||
if (!node.declaration.id || node.declaration.id.name !== 'createCollection') break;
|
if (!node.declaration.id || node.declaration.id.name !== 'createCollection') break;
|
||||||
createCollection = module.content.substring(node.start || 0, node.end || 0);
|
createCollection = module.content.substring(node.start || 0, node.end || 0);
|
||||||
|
|
||||||
// remove node
|
|
||||||
body.splice(i, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.splice(i, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'FunctionDeclaration': {
|
case 'FunctionDeclaration': {
|
||||||
|
@ -376,24 +375,23 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { start, end } = componentImport;
|
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) {
|
for (const componentImport of componentExports) {
|
||||||
const { start, end } = componentImport;
|
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) {
|
if (componentProps.length > 0) {
|
||||||
propsStatement = 'let {';
|
const shortname = path.posix.relative(compileOptions.astroConfig.projectRoot.pathname, state.filename);
|
||||||
for (const componentExport of componentProps) {
|
const props = componentProps.map(prop => (prop.id as Identifier)?.name).filter(v => v);
|
||||||
propsStatement += `${(componentExport.id as Identifier).name}`;
|
console.log();
|
||||||
const { init } = componentExport;
|
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
|
||||||
if (init) {
|
Please update your code to use:
|
||||||
propsStatement += `= ${babelGenerator(init).code}`;
|
|
||||||
}
|
const { ${props.join(', ')} } = Astro.props;\n`));
|
||||||
propsStatement += `,`;
|
|
||||||
}
|
|
||||||
propsStatement += `} = props;\n`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle createCollection, if any
|
// handle createCollection, if any
|
||||||
|
@ -448,12 +446,16 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
|
||||||
for (const [namespace, { spec }] of contentImports.entries()) {
|
for (const [namespace, { spec }] of contentImports.entries()) {
|
||||||
const globResult = fetchContent(spec, { namespace, filename: state.filename });
|
const globResult = fetchContent(spec, { namespace, filename: state.filename });
|
||||||
for (const importStatement of globResult.imports) {
|
for (const importStatement of globResult.imports) {
|
||||||
state.importExportStatements.add(importStatement);
|
state.importStatements.add(importStatement);
|
||||||
}
|
}
|
||||||
contentCode += globResult.code;
|
contentCode += globResult.code;
|
||||||
}
|
}
|
||||||
|
|
||||||
script = propsStatement + contentCode + babelGenerator(program).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 {
|
return {
|
||||||
|
@ -491,7 +493,7 @@ const FALSY_EXPRESSIONS = new Set(['false', 'null', 'undefined', 'void 0']);
|
||||||
/** Compile page markup */
|
/** Compile page markup */
|
||||||
async function compileHtml(enterNode: TemplateNode, state: CodegenState, compileOptions: CompileOptions): Promise<string> {
|
async function compileHtml(enterNode: TemplateNode, state: CodegenState, compileOptions: CompileOptions): Promise<string> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const { components, css, importExportStatements, filename, fileID } = state;
|
const { components, css, importStatements, exportStatements, filename, fileID } = state;
|
||||||
const { astroConfig } = compileOptions;
|
const { astroConfig } = compileOptions;
|
||||||
|
|
||||||
let paren = -1;
|
let paren = -1;
|
||||||
|
@ -570,8 +572,8 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
|
||||||
case 'InlineComponent': {
|
case 'InlineComponent': {
|
||||||
switch (node.name) {
|
switch (node.name) {
|
||||||
case 'Prism': {
|
case 'Prism': {
|
||||||
if (!importExportStatements.has(PRISM_IMPORT)) {
|
if (!importStatements.has(PRISM_IMPORT)) {
|
||||||
importExportStatements.add(PRISM_IMPORT);
|
importStatements.add(PRISM_IMPORT);
|
||||||
}
|
}
|
||||||
if (!components.has('Prism')) {
|
if (!components.has('Prism')) {
|
||||||
components.set('Prism', {
|
components.set('Prism', {
|
||||||
|
@ -634,7 +636,7 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
|
||||||
}
|
}
|
||||||
const { wrapper, wrapperImport } = getComponentWrapper(name, componentInfo, { astroConfig, filename });
|
const { wrapper, wrapperImport } = getComponentWrapper(name, componentInfo, { astroConfig, filename });
|
||||||
if (wrapperImport) {
|
if (wrapperImport) {
|
||||||
importExportStatements.add(wrapperImport);
|
importStatements.add(wrapperImport);
|
||||||
}
|
}
|
||||||
if (curr === 'markdown') {
|
if (curr === 'markdown') {
|
||||||
await pushMarkdownToBuffer();
|
await pushMarkdownToBuffer();
|
||||||
|
@ -777,7 +779,8 @@ export async function codegen(ast: Ast, { compileOptions, filename, fileID }: Co
|
||||||
markers: {
|
markers: {
|
||||||
insideMarkdown: false,
|
insideMarkdown: false,
|
||||||
},
|
},
|
||||||
importExportStatements: new Set(),
|
importStatements: new Set(),
|
||||||
|
exportStatements: new Set(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const { script, createCollection } = compileModule(ast.module, state, compileOptions);
|
const { script, createCollection } = compileModule(ast.module, state, compileOptions);
|
||||||
|
@ -788,7 +791,8 @@ export async function codegen(ast: Ast, { compileOptions, filename, fileID }: Co
|
||||||
|
|
||||||
return {
|
return {
|
||||||
script: script,
|
script: script,
|
||||||
imports: Array.from(state.importExportStatements),
|
imports: Array.from(state.importStatements),
|
||||||
|
exports: Array.from(state.exportStatements),
|
||||||
html,
|
html,
|
||||||
css: state.css.length ? state.css.join('\n\n') : undefined,
|
css: state.css.length ? state.css.join('\n\n') : undefined,
|
||||||
createCollection,
|
createCollection,
|
||||||
|
|
|
@ -105,6 +105,7 @@ interface CompileComponentOptions {
|
||||||
projectRoot: string;
|
projectRoot: string;
|
||||||
isPage?: boolean;
|
isPage?: boolean;
|
||||||
}
|
}
|
||||||
|
/** Compiles an Astro component */
|
||||||
export async function compileComponent(source: string, { compileOptions, filename, projectRoot, isPage }: CompileComponentOptions): Promise<CompileResult> {
|
export async function compileComponent(source: string, { compileOptions, filename, projectRoot, isPage }: CompileComponentOptions): Promise<CompileResult> {
|
||||||
const result = await transformFromSource(source, { compileOptions, filename, projectRoot });
|
const result = await transformFromSource(source, { compileOptions, filename, projectRoot });
|
||||||
const site = compileOptions.astroConfig.buildOptions.site || `http://localhost:${compileOptions.astroConfig.devOptions.port}`;
|
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');
|
const __astroInternal = Symbol('astro.internal');
|
||||||
async function __render(props, ...children) {
|
async function __render(props, ...children) {
|
||||||
const Astro = {
|
const Astro = {
|
||||||
|
props,
|
||||||
|
site: new URL('/', ${JSON.stringify(site)}),
|
||||||
css: props[__astroInternal]?.css || [],
|
css: props[__astroInternal]?.css || [],
|
||||||
request: props[__astroInternal]?.request || {},
|
request: props[__astroInternal]?.request || {},
|
||||||
site: new URL('/', ${JSON.stringify(site)}),
|
|
||||||
isPage: props[__astroInternal]?.isPage || false
|
isPage: props[__astroInternal]?.isPage || false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,11 +146,15 @@ export async function __renderPage({request, children, props, css}) {
|
||||||
__render,
|
__render,
|
||||||
};
|
};
|
||||||
|
|
||||||
props[__astroInternal] = {
|
Object.defineProperty(props, __astroInternal, {
|
||||||
request,
|
value: {
|
||||||
css,
|
request,
|
||||||
isPage: true
|
css,
|
||||||
};
|
isPage: true
|
||||||
|
},
|
||||||
|
writable: false,
|
||||||
|
enumerable: false
|
||||||
|
})
|
||||||
|
|
||||||
const childBodyResult = await currentChild.__render(props, children);
|
const childBodyResult = await currentChild.__render(props, children);
|
||||||
|
|
||||||
|
@ -162,7 +168,11 @@ export async function __renderPage({request, children, props, css}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return childBodyResult;
|
return childBodyResult;
|
||||||
};\n`;
|
};
|
||||||
|
|
||||||
|
${result.exports.join('\n')}
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result,
|
result,
|
||||||
|
|
|
@ -228,7 +228,7 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
|
||||||
canonicalURL: canonicalURL(requestURL.pathname, requestURL.origin),
|
canonicalURL: canonicalURL(requestURL.pathname, requestURL.origin),
|
||||||
},
|
},
|
||||||
children: [],
|
children: [],
|
||||||
props: { collection },
|
props: Object.keys(collection).length > 0 ? { collection } : {},
|
||||||
css: Array.isArray(mod.css) ? mod.css : typeof mod.css === 'string' ? [mod.css] : [],
|
css: Array.isArray(mod.css) ? mod.css : typeof mod.css === 'string' ? [mod.css] : [],
|
||||||
})) as string;
|
})) as string;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let content: any;
|
const { content } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
@ -14,4 +14,4 @@ export let content: any;
|
||||||
|
|
||||||
<main><slot></slot></main>
|
<main><slot></slot></main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let collection: any;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
export async function createCollection() {
|
export async function createCollection() {
|
||||||
const allPosts = Astro.fetchContent('./post/**/*.md');
|
const allPosts = Astro.fetchContent('./post/**/*.md');
|
||||||
|
@ -27,4 +27,4 @@ export async function createCollection() {
|
||||||
{collection.data.map((post) => (
|
{collection.data.map((post) => (
|
||||||
<a href={post.url}>{post.title}</a>
|
<a href={post.url}>{post.title}</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let collection: any;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
export async function createCollection() {
|
export async function createCollection() {
|
||||||
const allPosts = Astro.fetchContent('./post/*.md');
|
const allPosts = Astro.fetchContent('./post/*.md');
|
||||||
|
@ -26,4 +26,4 @@ export async function createCollection() {
|
||||||
|
|
||||||
<div id={collection.params.slug}>
|
<div id={collection.params.slug}>
|
||||||
<h1>{collection.data[0].title}</h1>
|
<h1>{collection.data[0].title}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let collection: any;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
export async function createCollection() {
|
export async function createCollection() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let collection: any;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
export async function createCollection() {
|
export async function createCollection() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let collection: any;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
export async function createCollection() {
|
export async function createCollection() {
|
||||||
const data = await Promise.all([
|
const data = await Promise.all([
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let collection: any;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
export async function createCollection() {
|
export async function createCollection() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let content;
|
const { content } = Astro.props;
|
||||||
---
|
---
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let collection;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
export function createCollection() {
|
export function createCollection() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
import { Markdown } from 'astro/components';
|
import { Markdown } from 'astro/components';
|
||||||
export const title = 'My Blog Post';
|
const title = 'My Blog Post';
|
||||||
export const description = 'This is a post about some stuff.';
|
const description = 'This is a post about some stuff.';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Markdown>
|
<Markdown>
|
||||||
|
|
|
@ -4,8 +4,8 @@ import Layout from '../layouts/content.astro';
|
||||||
import Hello from '../components/Hello.jsx';
|
import Hello from '../components/Hello.jsx';
|
||||||
import Counter from '../components/Counter.jsx';
|
import Counter from '../components/Counter.jsx';
|
||||||
|
|
||||||
export const title = 'My Blog Post';
|
const title = 'My Blog Post';
|
||||||
export const description = 'This is a post about some stuff.';
|
const description = 'This is a post about some stuff.';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Markdown>
|
<Markdown>
|
||||||
|
|
|
@ -4,8 +4,8 @@ import Layout from '../layouts/content.astro';
|
||||||
import Hello from '../components/Hello.jsx';
|
import Hello from '../components/Hello.jsx';
|
||||||
import Counter from '../components/Counter.jsx';
|
import Counter from '../components/Counter.jsx';
|
||||||
|
|
||||||
export const title = 'My Blog Post';
|
const title = 'My Blog Post';
|
||||||
export const description = 'This is a post about some stuff.';
|
const description = 'This is a post about some stuff.';
|
||||||
---
|
---
|
||||||
|
|
||||||
<div id="deep">
|
<div id="deep">
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { Markdown } from 'astro/components';
|
||||||
import Layout from '../layouts/content.astro';
|
import Layout from '../layouts/content.astro';
|
||||||
import Example from '../components/Example.jsx';
|
import Example from '../components/Example.jsx';
|
||||||
|
|
||||||
export const title = 'My Blog Post';
|
const title = 'My Blog Post';
|
||||||
export const description = 'This is a post about some stuff.';
|
const description = 'This is a post about some stuff.';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Markdown>
|
<Markdown>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
export let collection;
|
const { collection } = Astro.props;
|
||||||
|
|
||||||
export async function createCollection() {
|
export async function createCollection() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
---
|
---
|
||||||
import authorData from '../data/authors.json';
|
import authorData from '../data/authors.json';
|
||||||
|
|
||||||
export let authorId: string;
|
export interface Props {
|
||||||
|
authorId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { authorId } = Astro.props;
|
||||||
const author = authorData[authorId];
|
const author = authorData[authorId];
|
||||||
---
|
---
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
---
|
---
|
||||||
export let title: string;
|
export interface Props {
|
||||||
export let description: string;
|
title: string;
|
||||||
export let permalink: string;
|
description: string;
|
||||||
|
permalink: string;
|
||||||
|
}
|
||||||
|
const { title, description, permalink } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
---
|
---
|
||||||
export let author: string;
|
export interface Props {
|
||||||
export let source: string;
|
author: string;
|
||||||
export let sourceHref: string;
|
source: string;
|
||||||
|
sourceHref: string;
|
||||||
|
}
|
||||||
|
const { author, source, sourceHref } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<blockquote>
|
<blockquote>
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
import Author from './Author.astro';
|
import Author from './Author.astro';
|
||||||
import GithubStarButton from './GithubStarButton.astro';
|
import GithubStarButton from './GithubStarButton.astro';
|
||||||
|
|
||||||
export let title: string;
|
export interface Props {
|
||||||
export let author: string;
|
title: string;
|
||||||
export let publishDate: string;
|
author: string;
|
||||||
export let heroImage: string;
|
publishDate: string;
|
||||||
|
heroImage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { title, author, publishDate, heroImage } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
---
|
---
|
||||||
import Author from './Author.astro';
|
import Author from './Author.astro';
|
||||||
|
|
||||||
export let title: string;
|
export interface Props {
|
||||||
export let publishDate: string;
|
title: string;
|
||||||
export let href: string;
|
publishDate: string;
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { title, publishDate, href } = Astro.props;
|
||||||
---
|
---
|
||||||
<article class="post-preview">
|
<article class="post-preview">
|
||||||
<header>
|
<header>
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
---
|
---
|
||||||
export let type = "tip";
|
export interface Props {
|
||||||
export let title;
|
title: string;
|
||||||
|
type?: 'tip' | 'warning' | 'error'
|
||||||
|
}
|
||||||
|
const { type = 'tip', title } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<aside class={`note type-${type}`}>
|
<aside class={`note type-${type}`}>
|
||||||
|
|
|
@ -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">
|
<pre><code>{code.trim().split('\n').map(ln => <span class="line">
|
||||||
|
|
|
@ -4,7 +4,7 @@ import SiteSidebar from '../components/SiteSidebar.astro';
|
||||||
import ThemeToggle from '../components/ThemeToggle.tsx';
|
import ThemeToggle from '../components/ThemeToggle.tsx';
|
||||||
import DocSidebar from '../components/DocSidebar.tsx';
|
import DocSidebar from '../components/DocSidebar.tsx';
|
||||||
|
|
||||||
export let content;
|
const { content } = Astro.props;
|
||||||
const headers = content?.astro?.headers;
|
const headers = content?.astro?.headers;
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue