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:
Nate Moore 2021-06-24 17:48:24 -05:00 committed by GitHub
parent bc9e0f180c
commit a136c85e6b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 275 additions and 132 deletions

View 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}"!`);
---
```

View file

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

View file

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

View file

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

View file

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

View file

@ -34,7 +34,7 @@ The rendered Markdown content is placed into the default `<slot />` element.
```jsx
---
export let content;
const { content } = Astro.props;
---
<html>

View file

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

View file

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

View file

@ -1,5 +1,4 @@
---
export let content;
---
<html>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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');

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
---
export let title = 'Jeanine White: Personal Site';
const { title = 'Jeanine White: Personal Site' } = Astro.props;
---
<meta charset="UTF-8">

View file

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

View file

@ -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() {

View file

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

View file

@ -1,5 +1,5 @@
---
export let style;
const { style } = Astro.props;
---
<style lang="scss">

View file

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

View file

@ -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();

View file

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

View file

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

View file

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

View file

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

View file

@ -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} />`

View file

@ -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>
<pre class={className}><code class={className}>{html}</code></pre>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
---
export let collection: any;
const { collection } = Astro.props;
export async function createCollection() {
return {

View file

@ -1,5 +1,5 @@
---
export let collection: any;
const { collection } = Astro.props;
export async function createCollection() {
return {

View file

@ -1,5 +1,5 @@
---
export let collection: any;
const { collection } = Astro.props;
export async function createCollection() {
const data = await Promise.all([

View file

@ -1,5 +1,5 @@
---
export let collection: any;
const { collection } = Astro.props;
export async function createCollection() {
return {

View file

@ -1,5 +1,5 @@
---
export let content;
const { content } = Astro.props;
---
<html>
<head>

View file

@ -1,5 +1,5 @@
---
export let collection;
const { collection } = Astro.props;
export function createCollection() {
return {

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
---
export let collection;
const { collection } = Astro.props;
export async function createCollection() {
return {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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