diff options
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; --- |