From a2dca4675d8af6ca512df698046de8ce272c60b8 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Fri, 21 May 2021 13:15:49 -0500 Subject: [PATCH] example: update doc template --- .../src/pages/collections.astro | 198 +----------------- examples/astro-markdown/src/pages/index.astro | 2 +- examples/doc/public/code.css | 155 ++++++++++++++ examples/doc/public/index.css | 120 +++++++++-- examples/doc/src/components/CodeBlock.tsx | 10 - examples/doc/src/components/DocSidebar.astro | 13 -- examples/doc/src/components/DocSidebar.tsx | 55 +++++ .../doc/src/components/EditOnGithub.astro | 16 -- examples/doc/src/components/EditOnGithub.tsx | 13 ++ examples/doc/src/components/SiteSidebar.astro | 21 +- examples/doc/src/config.ts | 22 +- examples/doc/src/layouts/Main.astro | 10 +- examples/doc/src/pages/example.md | 35 ++++ examples/doc/src/pages/index.astro | 129 +----------- packages/astro/components/Prism.astro | 15 +- 15 files changed, 389 insertions(+), 425 deletions(-) create mode 100644 examples/doc/public/code.css delete mode 100644 examples/doc/src/components/CodeBlock.tsx delete mode 100644 examples/doc/src/components/DocSidebar.astro create mode 100644 examples/doc/src/components/DocSidebar.tsx delete mode 100644 examples/doc/src/components/EditOnGithub.astro create mode 100644 examples/doc/src/components/EditOnGithub.tsx create mode 100644 examples/doc/src/pages/example.md diff --git a/examples/astro-markdown/src/pages/collections.astro b/examples/astro-markdown/src/pages/collections.astro index efdf85d5e..a8c6148b3 100644 --- a/examples/astro-markdown/src/pages/collections.astro +++ b/examples/astro-markdown/src/pages/collections.astro @@ -9,203 +9,7 @@ import Markdown from 'astro/components/Markdown.astro';
- # 🍱 Collections - - ## ❓ What are Collections? - - [Fetching data is easy in Astro][docs-data]. But what if you wanted to make a paginated blog? What if you wanted an easy way to sort data, or filter data based on part of the URL? Or generate an RSS 2.0 feed? When you need something a little more powerful than simple data fetching, Astro’s Collections API may be what you need. - - An Astro Collection is similar to the general concept of Collections in static site generators like Jekyll, Hugo, Eleventy, etc. It’s a general way to load an entire data set. But one big difference between Astro Collections and traditional static site generators is: **Astro lets you seamlessly blend remote API data and local files in a JAMstack-friendly way.** To see how, this guide will walk through a few examples. If you’d like, you can reference the [blog example project][example-blog] to see the finished code in context. - - ## 🧑‍🎨 How to Use - - By default, any Astro component can fetch data from any API or local `*.md` files. But what if you had a blog you wanted to paginate? What if you wanted to generate dynamic URLs based on metadata (e.g. `/tag/:tag/`)? Or do both together? Astro Collections are a way to do all of that. It’s perfect for generating blog-like content, or scaffolding out dynamic URLs from your data. - - Let’s pretend we have some blog posts written already. This is our starting project structure: - - ``` - └── src/ - └── pages/ - └── post/ - └── (blog content) - ``` - - The first step in adding some dynamic collections is deciding on a URL schema. For our example website, we’re aiming for the following URLs: - - - `/post/:post`: A single blog post page - - `/posts/:page`: A list page of all blog posts, paginated, and sorted most recent first - - `/tag/:tag`: All blog posts, filtered by a specific tag - - Because `/post/:post` references the static files we have already, that doesn’t need to be a collection. But we will need collections for `/posts/:page` and `/tag/:tag` because those will be dynamically generated. For both collections we’ll create a `/src/pages/$[collection].astro` file. This is our new structure: - - ```diff - └── src/ - └── pages/ - ├── post/ - │ └── (blog content) - + ├── $posts.astro -> /posts/1, /posts/2, … - + └── $tag.astro -> /tag/:tag/1, /tag/:tag/2, … - ``` - - 💁‍ **Tip**: Any `.astro` filename beginning with a `$` is how it’s marked as a collection. - - In each `$[collection].astro` file, we’ll need 2 things: - - ```js - // 1. We need to mark “collection” as a prop (this is a special reserved name) - export let collection: any; - - // 2. We need to export an async createCollection() function that will retrieve our data. - export async function createCollection() { - return { - async data() { - // return data here to load (we’ll cover how later) - }, - }; - } - ``` - - These are important so your data is exposed to the page as a prop, and also Astro has everything it needs to gather your data and generate the proper routes. How it does this is more clear if we walk through a practical example. - - #### Example 1: Simple pagination - - Our blog posts all contain `title`, `tags`, and `published_at` in their frontmatter: - - ```md - --- - title: My Blog Post - tags: - - javascript - published_at: 2021-03-01 09:34:00 - --- - - # My Blog post - - … - ``` - - There’s nothing special or reserved about any of these names; you’re free to name everything whatever you’d like, or have as much or little frontmatter as you need. - - ```jsx - // /src/pages/$posts.astro - --- - export let collection: any; - - export async function createCollection() { - const allPosts = Astro.fetchContent('./post/*.md'); // load data that already lives at `/post/:slug` - allPosts.sort((a, b) => new Date(b.published_at) - new Date(a.published_at)); // sort newest -> oldest (we got "published_at" from frontmatter!) - - // (load more data here, if needed) - - return { - async data() { - return allPosts; - }, - pageSize: 10, // how many we want to show per-page (default: 25) - }; - } - - function formatDate(date) { - return new Date(date).toUTCString(); - } - --- - - - - Blog Posts: page {collection.page.current} - - - - - -
-
Results {collection.start + 1}–{collection.end + 1} of {collection.total}
- {collection.data.map((post) => ( -

{post.title}

- - Read - )} -
-
-

Page {collection.page.current} / {collection.page.last}

- -
- - - ``` - - Let’s walk through some of the key parts: - - - `export let collection`: this is important because it exposes a prop to the page for Astro to return with all your data loaded. ⚠️ **It must be named `collection`**. - - `export async function createCollection()`: this is also required, **and must be named this exactly.** This is an async function that lets you load data from anywhere (even a remote API!). At the end, you must return an object with `{ data: yourData }`. There are other options such as `pageSize` we’ll cover later. - - `{collection.data.map((post) => (…`: this lets us iterate over all the markdown posts. This will take the shape of whatever you loaded in `createCollection()`. It will always be an array. - - `{collection.page.current}`: this, and other properties, simply return more info such as what page a user is on, what the URL is, etc. etc. - - Curious about everything on `collection`? See the [reference][collection-api]. - - #### Example 2: Advanced filtering & pagination - - In our earlier example, we covered simple pagination for `/posts/1`, but we’d still like to make `/tag/:tag/1` and `/year/:year/1`. To do that, we’ll create 2 more collections: `/src/pages/$tag.astro` and `src/pages/$year.astro`. Assume that the markup is the same, but we’ve expanded the `createCollection()` function with more data. - - ```diff - // /src/pages/$tag.astro - --- - import Pagination from '../components/Pagination.astro'; - import PostPreview from '../components/PostPreview.astro'; - - export let collection: any; - - export async function createCollection() { - const allPosts = Astro.fetchContent('./post/*.md'); - allPosts.sort((a, b) => new Date(b.published_at) - new Date(a.published_at)); - + const allTags = [...new Set(allPosts.map((post) => post.tags).flat())]; // gather all unique tags (we got "tags" from frontmatter!) - + allTags.sort((a, b) => a.localeCompare(b)); // sort tags A -> Z - + const routes = allTags.map((tag) => ({ tag })); // this is where we set { params: { tag } } - - return { - - async data() { - - return allPosts; - + async data({ params }) { - + return allPosts.filter((post) => post.tags.includes(params.tag)); // filter posts that match the :tag from the URL ("params") - }, - pageSize: 10, - + routes, - + permalink: ({ params }) => `/tag/${params.tag}/` // this is where we generate our URL structure - }; - } - --- - ``` - - Some important concepts here: - - - `routes = allTags.map((tag) => ({ tag }))`: Astro handles pagination for you automatically. But when it needs to generate multiple routes, this is where you tell Astro about all the possible routes. This way, when you run `astro build`, your static build isn’t missing any pages. - - `` permalink: ({ params }) => `/tag/${params.tag}/` ``: this is where you tell Astro what the generated URL should be. Note that while you have control over this, the root of this must match the filename (it's best **NOT** to use `/pages/$tag.astro` to generate `/year/$year.astro`; that should live at `/pages/$year.astro` as a separate file). - - `allPosts.filter((post) => post.tag === params.tag)`: we aren’t returning all posts here; we’re only returning posts with a matching tag. _What tag,_ you ask? The `routes` array has `[{ tag: 'javascript' }, { tag: '…`, and all the routes we need to gather. So we first need to query everything, but only return the `.filter()`ed posts at the very end. - - Other things of note is that we are sorting like before, but we filter by the frontmatter `tag` property, and return those at URLs. - - These are still paginated, too! But since there are other conditions applied, they live at a different URL. - - #### Tips - - - Having to load different collections in different `$[collection].astro` files might seem like a pain at first, until you remember **you can create reusable components!** Treat `/pages/*.astro` files as your one-off routing & data fetching logic, and treat `/components/*.astro` as your reusable markup. If you find yourself duplicating things too much, you can probably use a component instead! - - Stay true to `/pages/$[collection].astro` naming. If you have an `/all-posts/*` route, then use `/pages/$all-posts.astro` to manage that. Don’t try and trick `permalink` to generate too many URL trees; it’ll only result in pages being missed when it comes time to build. - - ### 📚 Further Reading - - - [Fetching data in Astro][docs-data] - - API Reference: [collection][collection-api] - - API Reference: [createCollection()][create-collection-api] - - API Reference: [Creating an RSS feed][create-collection-api] - - [docs-data]: ../README.md#-fetching-data - [collection-api]: ./api.md#collection - [create-collection-api]: ./api.md#createcollection - [example-blog]: ../examples/blog - [fetch-content]: ./api.md#fetchcontent -
+
diff --git a/examples/astro-markdown/src/pages/index.astro b/examples/astro-markdown/src/pages/index.astro index cbff28b5c..a1cce3539 100644 --- a/examples/astro-markdown/src/pages/index.astro +++ b/examples/astro-markdown/src/pages/index.astro @@ -17,7 +17,7 @@ const items = ['A', 'B', 'C']; **Astro Markdown** brings native Markdown support to HTML! - > It's inspired by [`mdx`](https://mdxjs.com/) and powered by [`remark`](https://github.com/remarkjs/remark)). + > It's inspired by [`mdx`](https://mdxjs.com/) and powered by [`remark`](https://github.com/remarkjs/remark). The best part? It comes with all the Astro features you expect. diff --git a/examples/doc/public/code.css b/examples/doc/public/code.css new file mode 100644 index 000000000..54b2c5094 --- /dev/null +++ b/examples/doc/public/code.css @@ -0,0 +1,155 @@ +.language-css > code, +.language-sass > code, +.language-scss > code { + color: #fd9170; +} + +[class*="language-"] .namespace { + opacity: 0.7; +} + +.token.atrule { + color: #c792ea; +} + +.token.attr-name { + color: #ffcb6b; +} + +.token.attr-value { + color: #a5e844; +} + +.token.attribute { + color: #a5e844; +} + +.token.boolean { + color: #c792ea; +} + +.token.builtin { + color: #ffcb6b; +} + +.token.cdata { + color: #80cbc4; +} + +.token.char { + color: #80cbc4; +} + +.token.class { + color: #ffcb6b; +} + +.token.class-name { + color: #f2ff00; +} + +.token.comment { + color: #616161; +} + +.token.constant { + color: #c792ea; +} + +.token.deleted { + color: #ff6666; +} + +.token.doctype { + color: #616161; +} + +.token.entity { + color: #ff6666; +} + +.token.function { + color: #c792ea; +} + +.token.hexcode { + color: #f2ff00; +} + +.token.id { + color: #c792ea; + font-weight: bold; +} + +.token.important { + color: #c792ea; + font-weight: bold; +} + +.token.inserted { + color: #80cbc4; +} + +.token.keyword { + color: #c792ea; +} + +.token.number { + color: #fd9170; +} + +.token.operator { + color: #89ddff; +} + +.token.prolog { + color: #616161; +} + +.token.property { + color: #80cbc4; +} + +.token.pseudo-class { + color: #a5e844; +} + +.token.pseudo-element { + color: #a5e844; +} + +.token.punctuation { + color: #89ddff; +} + +.token.regex { + color: #f2ff00; +} + +.token.selector { + color: #ff6666; +} + +.token.string { + color: #a5e844; +} + +.token.symbol { + color: #c792ea; +} + +.token.tag { + color: #ff6666; +} + +.token.unit { + color: #fd9170; +} + +.token.url { + color: #ff6666; +} + +.token.variable { + color: #ff6666; +} diff --git a/examples/doc/public/index.css b/examples/doc/public/index.css index 7479cebba..c5ae87f5c 100644 --- a/examples/doc/public/index.css +++ b/examples/doc/public/index.css @@ -1,4 +1,5 @@ @import './theme'; +@import './code'; * { box-sizing: border-box; @@ -83,6 +84,9 @@ a { font-weight: 400; text-underline-offset: 0.08em; text-decoration: none; + display: inline-flex; + align-items: center; + gap: 0.5rem; } a > code:not([class*="language"]) { @@ -149,36 +153,22 @@ pre { background-color: var(--theme-code-bg); color: var(--theme-code-text); --padding-block: 1rem; - --padding-inline: 1.25rem; + --padding-inline: 2rem; padding: var(--padding-block) var(--padding-inline); padding-right: calc(var(--padding-inline) * 2); - margin-left: calc(var(--padding-inline) * -1); - margin-right: calc(var(--padding-inline) * -1); + margin-left: calc(50vw - var(--padding-inline)); + transform: translateX(-50vw); + line-height: 1.414; + width: calc(100vw + 4px); + max-width: calc(100% + (var(--padding-inline) * 2)); + overflow-y: hidden; overflow-x: auto; - max-width: 100vw; -} -pre::before, -pre::after { - position: absolute; - content: ''; - display: block; - pointer-events: none; - top: 0; - bottom: 0; - height: 100%; - width: var(--padding-inline); - background: red; -} -pre::before { - left: 0; -} -pre::after { - right: 0; } @media (min-width: 37.75em) { pre { + --padding-inline: 1.25rem; border-radius: 8px; } } @@ -272,3 +262,89 @@ input[name="theme-toggle"] { left: 0; z-index: -1; } + +nav h4 { + font-weight: 400; + font-size: 1.25rem; + margin: 0; + margin-bottom: 1em; +} + +.edit-on-github, +.header-link { + font-size: 1rem; + padding-left: 1rem; + border-left: 4px solid var(--theme-divider); +} + +.edit-on-github:hover, +.edit-on-github:focus, +.header-link:hover, +.header-link:focus { + color: var(--theme-text-light); + border-left-color: var(--theme-text-lighter); +} + +.header-link:focus-within { + color: var(--theme-text-light); + border-left-color: var(--theme-text-lighter); +} + +.header-link.active { + border-left-color: var(--theme-accent); + color: var(--theme-accent); +} + +.header-link.depth-2 { + font-weight: 600; +} + +.header-link.depth-3 { + padding-left: 2rem; +} +.header-link.depth-4 { + padding-left: 3rem; +} + +.edit-on-github, +.header-link a { + font: inherit; + color: inherit; + text-decoration: none; +} + +.edit-on-github { + margin-top: 2rem; + text-decoration: none; +} +.edit-on-github > * { + text-decoration: none; +} + +.nav-link { + font-size: 1rem; + margin-bottom: 0; + transform: translateX(0); + transition: 120ms transform ease-out; +} + +.nav-link:hover, +.nav-link:focus { + color: var(--theme-text-lighter); + transform: translateX(0.25em); +} + +.nav-link:focus-within { + color: var(--theme-text-lighter); + transform: translateX(0.25em); +} + +.nav-link a { + font: inherit; + color: inherit; + text-decoration: none; +} + +.nav-groups > li + li { + margin-top: 2rem; +} diff --git a/examples/doc/src/components/CodeBlock.tsx b/examples/doc/src/components/CodeBlock.tsx deleted file mode 100644 index 6990e2e53..000000000 --- a/examples/doc/src/components/CodeBlock.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { FunctionalComponent } from 'preact'; -import { h, Fragment } from 'preact'; - -const CodeBlock: FunctionalComponent = ({ children }) => { - return
- {children} -
-} - -export default CodeBlock; diff --git a/examples/doc/src/components/DocSidebar.astro b/examples/doc/src/components/DocSidebar.astro deleted file mode 100644 index a23ec93d8..000000000 --- a/examples/doc/src/components/DocSidebar.astro +++ /dev/null @@ -1,13 +0,0 @@ ---- -import EditOnGithub from './EditOnGithub.astro'; ---- - - diff --git a/examples/doc/src/components/DocSidebar.tsx b/examples/doc/src/components/DocSidebar.tsx new file mode 100644 index 000000000..59703269d --- /dev/null +++ b/examples/doc/src/components/DocSidebar.tsx @@ -0,0 +1,55 @@ +import type { FunctionalComponent } from 'preact'; +import { h } from 'preact'; +import { useState, useEffect, useRef } from 'preact/hooks'; +import EditOnGithub from './EditOnGithub'; + +const DocSidebar: FunctionalComponent<{ headers: any[]; editHref: string; }> = ({ headers, editHref }) => { + const itemOffsets = useRef([]); + const [activeId, setActiveId] = useState(undefined); + + useEffect(() => { + const getItemOffsets = () => { + const titles = document.querySelectorAll('article :is(h2, h3, h4)'); + itemOffsets.current = Array.from(titles).map(title => ({ + id: title.id, + topOffset: title.getBoundingClientRect().top + window.scrollY + })); + } + + const onScroll = () => { + const itemIndex = itemOffsets.current.findIndex(item => item.topOffset > window.scrollY + (window.innerHeight / 3)); + if (itemIndex === 0) { + setActiveId(undefined); + } else if (itemIndex === -1) { + setActiveId(itemOffsets.current[itemOffsets.current.length - 1].id) + } else { + setActiveId(itemOffsets.current[itemIndex - 1].id) + } + } + + getItemOffsets(); + window.addEventListener('resize', getItemOffsets); + window.addEventListener('scroll', onScroll); + + return () => { + window.removeEventListener('resize', getItemOffsets); + window.removeEventListener('scroll', onScroll); + } + }, []); + + return ( + + ); +} + +export default DocSidebar; diff --git a/examples/doc/src/components/EditOnGithub.astro b/examples/doc/src/components/EditOnGithub.astro deleted file mode 100644 index 59515fa42..000000000 --- a/examples/doc/src/components/EditOnGithub.astro +++ /dev/null @@ -1,16 +0,0 @@ ---- -export let href: string; ---- - - - - Edit on GitHub - - - diff --git a/examples/doc/src/components/EditOnGithub.tsx b/examples/doc/src/components/EditOnGithub.tsx new file mode 100644 index 000000000..e39dafce3 --- /dev/null +++ b/examples/doc/src/components/EditOnGithub.tsx @@ -0,0 +1,13 @@ +import type { FunctionalComponent } from 'preact'; +import { h } from 'preact'; + +const EditOnGithub: FunctionalComponent<{ href: string }> = ({ href }) => { + return ( + + + Edit on GitHub + + ); +} + +export default EditOnGithub; diff --git a/examples/doc/src/components/SiteSidebar.astro b/examples/doc/src/components/SiteSidebar.astro index 2817a3ac9..7279d9aea 100644 --- a/examples/doc/src/components/SiteSidebar.astro +++ b/examples/doc/src/components/SiteSidebar.astro @@ -3,19 +3,18 @@ import { sidebar } from '../config.ts'; --- diff --git a/examples/doc/src/config.ts b/examples/doc/src/config.ts index 568f92243..645ea9f61 100644 --- a/examples/doc/src/config.ts +++ b/examples/doc/src/config.ts @@ -2,26 +2,8 @@ export const sidebar = [ { text: 'Introduction', children: [ - { text: 'What is Astro?', link: '/' }, - { text: 'Getting Started', link: '/guide/getting-started' }, - { text: 'Configuration', link: '/guide/configuration' }, - { text: 'Asset Handling', link: '/guide/assets' }, - { text: 'Markdown Extensions', link: '/guide/markdown' }, - { text: 'Using Vue in Markdown', link: '/guide/using-vue' }, - { text: 'Deploying', link: '/guide/deploy' } - ] - }, - { - text: 'Advanced', - children: [ - { text: 'Frontmatter', link: '/guide/frontmatter' }, - { text: 'Global Computed', link: '/guide/global-computed' }, - { text: 'Global Component', link: '/guide/global-component' }, - { text: 'Customization', link: '/guide/customization' }, - { - text: 'Differences from Vuepress', - link: '/guide/differences-from-vuepress' - } + { text: 'Welcome', link: '/' }, + { text: 'Example', link: '/example' } ] } ] diff --git a/examples/doc/src/layouts/Main.astro b/examples/doc/src/layouts/Main.astro index 4a9742310..d9a5405a0 100644 --- a/examples/doc/src/layouts/Main.astro +++ b/examples/doc/src/layouts/Main.astro @@ -1,10 +1,14 @@ --- import ArticleFooter from '../components/ArticleFooter.astro'; import SiteSidebar from '../components/SiteSidebar.astro'; -import DocSidebar from '../components/DocSidebar.astro'; import ThemeToggle from '../components/ThemeToggle.tsx'; +import DocSidebar from '../components/DocSidebar.tsx'; export let content; +const headers = content?.astro?.headers; +let editHref = Astro?.request?.url?.pathname?.slice(1) ?? ''; +if (editHref === '') editHref = `index`; +editHref = `https://github.com/snowpackjs/astro/tree/main/examples/doc/src/pages/${editHref}.md` --- @@ -147,7 +151,7 @@ export let content; @media (min-width: 88em) { .layout { - grid-template-columns: minmax(var(--gutter), 1fr) 20rem minmax(0, var(--max-width)) 12rem minmax(var(--gutter), 1fr); + grid-template-columns: minmax(var(--gutter), 1fr) 20rem minmax(0, var(--max-width)) 16rem minmax(var(--gutter), 1fr); padding-left: 0; padding-right: 0; } @@ -210,7 +214,7 @@ export let content; diff --git a/examples/doc/src/pages/example.md b/examples/doc/src/pages/example.md new file mode 100644 index 000000000..0966595d8 --- /dev/null +++ b/examples/doc/src/pages/example.md @@ -0,0 +1,35 @@ +--- +layout: ../layouts/Main.astro +--- + +# Markdown example + +This is a fully-featured page, written in Markdown! + +## Section A + +Lorem ipsum dolor sit amet, **consectetur adipiscing elit**. Sed ut tortor _suscipit_, posuere ante id, vulputate urna. Pellentesque molestie aliquam dui sagittis aliquet. Sed sed felis convallis, lacinia lorem sit amet, fermentum ex. Etiam hendrerit mauris at elementum egestas. Vivamus id gravida ante. Praesent consectetur fermentum turpis, quis blandit tortor feugiat in. Aliquam erat volutpat. In elementum purus et tristique ornare. Suspendisse sollicitudin dignissim est a ultrices. Pellentesque sed ipsum finibus, condimentum metus eget, sagittis elit. Sed id lorem justo. Vivamus in sem ac mi molestie ornare. + +## Section B + +Nam quam dolor, pellentesque sed odio euismod, feugiat tempus tellus. Quisque arcu velit, ultricies in faucibus sed, ultrices ac enim. Nunc eget dictum est. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ex nisi, egestas mollis ultricies ut, laoreet suscipit libero. Nam condimentum molestie turpis. Sed vestibulum sagittis congue. Maecenas tristique enim et tincidunt tempor. Curabitur ac scelerisque nulla, in malesuada libero. Praesent eu tempus odio. Pellentesque aliquam ullamcorper quam at gravida. Sed non fringilla mauris. Aenean sit amet ultrices erat. Vestibulum congue venenatis tortor, nec suscipit tortor. Aenean pellentesque mauris eget tortor tincidunt pharetra. + +## Section C + +```markdown +--- +layout: ../layouts/Main.astro +--- + +# Markdown example + +This is a fully-featured page, written in Markdown! + +## Section A + +Lorem ipsum dolor sit amet, **consectetur adipiscing elit**. Sed ut tortor _suscipit_, posuere ante id, vulputate urna. Pellentesque molestie aliquam dui sagittis aliquet. Sed sed felis convallis, lacinia lorem sit amet, fermentum ex. Etiam hendrerit mauris at elementum egestas. Vivamus id gravida ante. Praesent consectetur fermentum turpis, quis blandit tortor feugiat in. Aliquam erat volutpat. In elementum purus et tristique ornare. Suspendisse sollicitudin dignissim est a ultrices. Pellentesque sed ipsum finibus, condimentum metus eget, sagittis elit. Sed id lorem justo. Vivamus in sem ac mi molestie ornare. + +## Section B + +Nam quam dolor, pellentesque sed odio euismod, feugiat tempus tellus. Quisque arcu velit, ultricies in faucibus sed, ultrices ac enim. Nunc eget dictum est. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ex nisi, egestas mollis ultricies ut, laoreet suscipit libero. Nam condimentum molestie turpis. Sed vestibulum sagittis congue. Maecenas tristique enim et tincidunt tempor. Curabitur ac scelerisque nulla, in malesuada libero. Praesent eu tempus odio. Pellentesque aliquam ullamcorper quam at gravida. Sed non fringilla mauris. Aenean sit amet ultrices erat. Vestibulum congue venenatis tortor, nec suscipit tortor. Aenean pellentesque mauris eget tortor tincidunt pharetra. +``` diff --git a/examples/doc/src/pages/index.astro b/examples/doc/src/pages/index.astro index c3d2d4345..fe00e15a8 100644 --- a/examples/doc/src/pages/index.astro +++ b/examples/doc/src/pages/index.astro @@ -1,137 +1,14 @@ --- import Markdown from 'astro/components/Markdown.astro'; -import Prism from 'astro/components/Prism.astro'; - import Layout from '../layouts/Main.astro'; -import Note from '../components/Note.astro'; -import CodeBlock from '../components/CodeBlock.tsx'; --- - + - # ✍️ Markdown - - Astro comes with out-of-the-box Markdown support powered by the expansive [**remark**](https://github.com/remarkjs/remark) ecosystem. - - ## Remark Plugins - - **This is the first draft of Markdown support!** While we plan to support user-provided `remark` plugins soon, our hope is that you won't need `remark` plugins at all! - - In addition to [custom components inside the `` component](#markdown-component), Astro comes with [GitHub-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants), and syntax highlighting via [Prism](https://prismjs.com/) pre-enabled. These features are likely to be configurable in the future. - - ### Markdown Pages - - Astro treats any `.md` files inside of the `/src/pages` directory as pages. These pages are processed as plain Markdown files and do not support components. If you're looking to embed rich components in your Markdown, take a look at the [Markdown Component](#markdown-component) section. - - #### `layout` - - The only special Frontmatter key is `layout`, which defines the relative path to a `.astro` component which should wrap your Markdown content. - - `src/pages/index.md` - - ```md - --- - layout: ../layouts/main.astro - --- - # Hello world! - ``` - Layout files are normal `.astro` components. Any Frontmatter defined in your `.md` page will be exposed to the Layout component as the `content` prop. `content` also has an `astro` key which holds special metadata about your file, like the complete Markdown `source` and a `headings` object. + This is a starter template, have fun building your next documentation site with [Astro](https://astro.build). - The rendered Markdown content is placed into the default `` element. - - `src/layouts/main.astro` - - - ```jsx - --- - export let content; - --- - - - - {content.title} - - - - - - - ``` - - - ### Markdown Component - - Similar to tools like [MDX](https://mdxjs.com/) or [MDsveX](https://github.com/pngwn/MDsveX), Astro makes it straightforward to embed rich, interactive components inside of your Markdown content. The `` component is statically rendered, so it does not add any runtime overhead. - - Astro exposes a special `Markdown` component for `.astro` files which enables markdown syntax for its children **recursively**. Within the `Markdown` component you may also use plain HTML or any other type of component that is supported by Astro. - - ````jsx - --- - // For now, this import _must_ be named "Markdown" and _must not_ be wrapped with a custom component - // We're working on easing these restrictions! - import Markdown from 'astro/components/Markdown.astro'; - import Layout from '../layouts/main.astro'; - import MyFancyCodePreview from '../components/MyFancyCodePreview.tsx'; - - const expressions = 'Lorem ipsum'; - --- - - - - # Hello world! - - **Everything** supported in a `.md` file is also supported here! - - There is _zero_ runtime overhead. - - In addition, Astro supports: - - Astro {expressions} - - Automatic indentation normalization - - Automatic escaping of expressions inside code blocks - - ```jsx - // This content is not transformed! - const object = { someOtherValue }; - ``` - - - Rich component support like any `.astro` file! - - Recursive Markdown support (Component children are also processed as Markdown) - - - ```jsx - const object = { someOtherValue }; - ``` - - - - ```` - - ### Remote Markdown - - If you have Markdown in a remote source, you may pass it directly to the Markdown component. For example, the example below fetches the README from Snowpack's GitHub repository and renders it as HTML. - - ```jsx - --- - import Markdown from 'astro/components/Markdown.astro'; - - const content = await fetch('https://raw.githubusercontent.com/snowpackjs/snowpack/main/README.md').then(res => res.text()); - --- - - - {content} - - ``` - - ### Security FAQs - - **Aren't there security concerns to rendering remote markdown directly to HTML?** - - Yes! Just like with regular HTML, improper use the `` component can open you up to a [cross-site scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) attack. If you are rendering untrusted content, be sure to _santize your content **before** rendering it_. - - **Why not use a prop like React's `dangerouslySetInnerHTML={{ __html: content }}`?** - - Rendering a string of HTML (or Markdown) is an extremely common use case when rendering a static site and you probably don't need the extra hoops to jump through. Rendering untrusted content is always dangerous! Be sure to _santize your content **before** rendering it_. + It offers the right mix of simple-to-use [Markdown pages](/example) and rich, interactive components embedded in `.astro` files using ``. diff --git a/packages/astro/components/Prism.astro b/packages/astro/components/Prism.astro index 6b73d5bbc..5462cba0e 100644 --- a/packages/astro/components/Prism.astro +++ b/packages/astro/components/Prism.astro @@ -10,12 +10,12 @@ const languageMap = new Map([ ['ts', 'typescript'] ]); -if(lang == null) { +if (lang == null) { console.warn('Prism.astro: No language provided.'); } const ensureLoaded = lang => { - if(!Prism.languages[lang]) { + if(lang && !Prism.languages[lang]) { loadLanguages([lang]); } }; @@ -30,14 +30,17 @@ if(languageMap.has(lang)) { ensureLoaded(lang); } -if(!Prism.languages[lang]) { +if(lang && !Prism.languages[lang]) { console.warn(`Unable to load the language: ${lang}`); } const grammar = Prism.languages[lang]; -let html = Prism.highlight(code, grammar, lang); +let html = code; +if (grammar) { + html = Prism.highlight(code, grammar, lang); +} -let className = `language-${lang}`; +let className = lang ? `language-${lang}` : ''; --- -
{html}
+
{html}