Skip to content

Commit

Permalink
New Markdown API (#2862)
Browse files Browse the repository at this point in the history
* Implement new markdown plugin with deferred markdown rendering

* feat: switch from `getContent()` fn to `<Content />` API

* update types

* Update packages/astro/src/@types/astro.ts

Co-authored-by: Nate Moore <[email protected]>

* update types

* Create forty-coins-attend.md

Co-authored-by: Nate Moore <[email protected]>
Co-authored-by: Nate Moore <[email protected]>
  • Loading branch information
3 people authored Mar 29, 2022
1 parent 7d29fea commit 4299ab3
Show file tree
Hide file tree
Showing 27 changed files with 277 additions and 193 deletions.
16 changes: 16 additions & 0 deletions .changeset/forty-coins-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"astro": minor
---

Implement RFC [#0017](https://github.com/withastro/rfcs/blob/main/proposals/0017-markdown-content-redesign.md)

- New Markdown API
- New `Astro.glob()` API
- **BREAKING CHANGE:** Removed `Astro.fetchContent()` (replaced by `Astro.glob()`)

```diff
// v0.25
- let allPosts = Astro.fetchContent('./posts/*.md');
// v0.26+
+ let allPosts = await Astro.glob('./posts/*.md');
```
11 changes: 6 additions & 5 deletions examples/blog-multiple-authors/src/components/PostPreview.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface Props {
author: string;
}
const { post, author } = Astro.props;
const { frontmatter } = post;
function formatDate(date) {
return new Date(date).toUTCString().replace(/(\d\d\d\d) .*/, '$1'); // remove everything after YYYY
Expand All @@ -12,12 +13,12 @@ function formatDate(date) {

<article class="post">
<div class="data">
<h2>{post.title}</h2>
<a class="author" href={`/authors/${post.author}`}>{author.name}</a>
<time class="date" datetime={post.date}>{formatDate(post.date)}</time>
<h2>{frontmatter.title}</h2>
<a class="author" href={`/authors/${frontmatter.author}`}>{author.name}</a>
<time class="date" datetime={frontmatter.date}>{formatDate(frontmatter.date)}</time>
<p class="description">
{post.description}
<a class="link" href={post.url} aria-label={`Read ${post.title}`}>Read</a>
{frontmatter.description}
<a class="link" href={post.url} aria-label={`Read ${frontmatter.title}`}>Read</a>
</p>
</div>
</article>
Expand Down
20 changes: 6 additions & 14 deletions examples/blog-multiple-authors/src/pages/authors/[author].astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,28 @@
import MainHead from '../../components/MainHead.astro';
import Nav from '../../components/Nav.astro';
import PostPreview from '../../components/PostPreview.astro';
import Pagination from '../../components/Pagination.astro';
import authorData from '../../data/authors.json';
export function getStaticPaths() {
const allPosts = Astro.fetchContent<MarkdownFrontmatter>('../post/*.md');
let allAuthorsUnique = [...new Set(allPosts.map((p) => p.author))];
export async function getStaticPaths() {
const allPosts = await Astro.glob('../post/*.md');
let allAuthorsUnique = [...new Set(allPosts.map((p) => p.frontmatter.author))];
return allAuthorsUnique.map((author) => ({ params: { author }, props: { allPosts } }));
}
interface MarkdownFrontmatter {
date: number;
description: string;
title: string;
author: string;
}
const { allPosts } = Astro.props;
const { params, canonicalURL } = Astro.request;
const title = 'Don’s Blog';
const description = 'An example blog on Astro';
/** filter posts by author, sort by date */
const posts = allPosts.filter((post) => post.author === params.author).sort((a, b) => new Date(b.date).valueOf() - new Date(a.date).valueOf());
const author = authorData[posts[0].author];
const posts = allPosts.filter((post) => post.frontmatter.author === params.author).sort((a, b) => new Date(b.frontmatter.date).valueOf() - new Date(a.frontmatter.date).valueOf());
const author = authorData[posts[0].frontmatter.author];
---

<html lang="en">
<head>
<title>{title}</title>
<MainHead {title} {description} image={posts[0].image} canonicalURL={canonicalURL.toString()} />
<MainHead {title} {description} image={posts[0].frontmatter.image} canonicalURL={canonicalURL.toString()} />

<style lang="scss">
.title {
Expand Down
15 changes: 4 additions & 11 deletions examples/blog-multiple-authors/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ import PostPreview from '../components/PostPreview.astro';
import Pagination from '../components/Pagination.astro';
import authorData from '../data/authors.json';
interface MarkdownFrontmatter {
date: number;
image: string;
author: string;
}
// Component Script:
// You can write any JavaScript/TypeScript that you'd like here.
// It will run during the build, but never in the browser.
Expand All @@ -21,25 +15,24 @@ let description = 'An example blog on Astro';
let canonicalURL = Astro.request.canonicalURL;
// Data Fetching: List all Markdown posts in the repo.
let allPosts = Astro.fetchContent<MarkdownFrontmatter>('./post/*.md');
allPosts.sort((a, b) => new Date(b.date).valueOf() - new Date(a.date).valueOf());
let allPosts = await Astro.glob('./post/*.md');
allPosts.sort((a, b) => new Date(b.frontmatter.date).valueOf() - new Date(a.frontmatter.date).valueOf());
let firstPage = allPosts.slice(0, 2);
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
---

<html lang="en">
<head>
<title>{title}</title>
<MainHead {title} {description} image={allPosts[0].image} {canonicalURL} />
<MainHead {title} {description} image={allPosts[0].frontmatter.image} {canonicalURL} />
</head>

<body>
<Nav {title} />

<main class="wrapper">
{allPosts.map((post) => <PostPreview post={post} author={authorData[post.author]} />)}
{allPosts.map((post) => <PostPreview post={post} author={authorData[post.frontmatter.author]} />)}
</main>

<footer>
Expand Down
16 changes: 4 additions & 12 deletions examples/blog-multiple-authors/src/pages/posts/[...page].astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import Pagination from '../../components/Pagination.astro';
import authorData from '../../data/authors.json';
export async function getStaticPaths({ paginate, rss }) {
const allPosts = Astro.fetchContent<MarkdownFrontmatter>('../post/*.md');
const sortedPosts = allPosts.sort((a, b) => new Date(b.date).valueOf() - new Date(a.date).valueOf());
const allPosts = await Astro.glob('../post/*.md');
const sortedPosts = allPosts.sort((a, b) => new Date(b.frontmatter.date).valueOf() - new Date(a.frontmatter.date).valueOf());
// Generate an RSS feed from this collection of posts.
// NOTE: This is disabled by default, since it requires `buildOptions.site` to be set in your "astro.config.mjs" file.
Expand All @@ -31,21 +31,13 @@ export async function getStaticPaths({ paginate, rss }) {
let title = 'Don’s Blog';
let description = 'An example blog on Astro';
let canonicalURL = Astro.request.canonicalURL;
// collection
interface MarkdownFrontmatter {
date: number;
description: string;
title: string;
}
const { page } = Astro.props;
---

<html lang="en">
<head>
<title>{title}</title>
<MainHead {title} {description} image={page.data[0].image} canonicalURL={canonicalURL.toString()} prev={page.url.prev} next={page.url.next} />
<MainHead {title} {description} image={page.data[0].frontmatter.image} canonicalURL={canonicalURL.toString()} prev={page.url.prev} next={page.url.next} />

<style lang="scss">
.title {
Expand All @@ -70,7 +62,7 @@ const { page } = Astro.props;
<main class="wrapper">
<h2 class="title">All Posts</h2>
<small class="count">{page.start + 1}{page.end + 1} of {page.total}</small>
{page.data.map((post) => <PostPreview post={post} author={authorData[post.author]} />)}
{page.data.map((post) => <PostPreview post={post} author={authorData[post.frontmatter.author]} />)}
</main>

<footer>
Expand Down
6 changes: 3 additions & 3 deletions examples/blog/src/components/BlogPostPreview.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ const { post } = Astro.props;

<article class="post-preview">
<header>
<p class="publish-date">{post.publishDate}</p>
<a href={post.url}><h1 class="title">{post.title}</h1></a>
<p class="publish-date">{post.frontmatter.publishDate}</p>
<a href={post.url}><h1 class="title">{post.frontmatter.title}</h1></a>
</header>
<p>{post.description}</p>
<p>{post.frontmatter.description}</p>
<a href={post.url}>Read more</a>
</article>

Expand Down
8 changes: 2 additions & 6 deletions examples/blog/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import BaseHead from '../components/BaseHead.astro';
import BlogHeader from '../components/BlogHeader.astro';
import BlogPostPreview from '../components/BlogPostPreview.astro';
interface MarkdownFrontmatter {
publishDate: number;
}
// Component Script:
// You can write any JavaScript/TypeScript that you'd like here.
// It will run during the build, but never in the browser.
Expand All @@ -18,8 +14,8 @@ let permalink = 'https://example.com/';
// Data Fetching: List all Markdown posts in the repo.
let allPosts = await Astro.fetchContent('./posts/*.md');
allPosts = allPosts.sort((a, b) => new Date(b.publishDate).valueOf() - new Date(a.publishDate).valueOf());
let allPosts = await Astro.glob('./posts/*.md');
allPosts = allPosts.sort((a, b) => new Date(b.frontmatter.publishDate).valueOf() - new Date(a.frontmatter.publishDate).valueOf());
// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
Expand Down
9 changes: 5 additions & 4 deletions examples/portfolio/src/components/PortfolioPreview/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { h } from 'preact';
import Styles from './styles.module.scss';

function PortfolioPreview({ project }) {
const { frontmatter } = project;
return (
<div className={Styles.card}>
<div className={Styles.titleCard} style={`background-image:url(${project.img})`}>
<h1 className={Styles.title}>{project.title}</h1>
<div className={Styles.titleCard} style={`background-image:url(${frontmatter.img})`}>
<h1 className={Styles.title}>{frontmatter.title}</h1>
</div>
<div className="pa3">
<p className={`${Styles.desc} mt0 mb2`}>{project.description}</p>
<p className={`${Styles.desc} mt0 mb2`}>{frontmatter.description}</p>
<div className={Styles.tags}>
Tagged:
{project.tags.map((t) => (
{frontmatter.tags.map((t) => (
<div className={Styles.tag} data-tag={t}>
{t}
</div>
Expand Down
2 changes: 1 addition & 1 deletion examples/portfolio/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Footer from '../components/Footer/index.jsx';
import PortfolioPreview from '../components/PortfolioPreview/index.jsx';
// Data Fetching: List all Markdown posts in the repo.
const projects = Astro.fetchContent('./project/**/*.md');
const projects = await Astro.glob('./project/**/*.md');
const featuredProject = projects[0];
// Full Astro Component Syntax:
Expand Down
10 changes: 3 additions & 7 deletions examples/portfolio/src/pages/projects.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@ import Footer from '../components/Footer/index.jsx';
import Nav from '../components/Nav/index.jsx';
import PortfolioPreview from '../components/PortfolioPreview/index.jsx';
interface MarkdownFrontmatter {
publishDate: number;
}
const projects = Astro.fetchContent<MarkdownFrontmatter>('./project/**/*.md')
.filter(({ publishDate }) => !!publishDate)
.sort((a, b) => new Date(b.publishDate).valueOf() - new Date(a.publishDate).valueOf());
const projects = (await Astro.glob('./project/**/*.md'))
.filter(({ frontmatter }) => !!frontmatter.publishDate)
.sort((a, b) => new Date(b.frontmatter.publishDate).valueOf() - new Date(a.frontmatter.publishDate).valueOf());
---

<html lang="en">
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="vite/client" />

type Astro = import('./dist/types/@types/astro').AstroGlobal;
type Astro = import('astro').AstroGlobal;

// We duplicate the description here because editors won't show the JSDoc comment from the imported type (but will for its properties, ex: Astro.request will show the AstroGlobal.request description)
/**
Expand Down
2 changes: 2 additions & 0 deletions packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"@proload/core": "^0.2.2",
"@proload/plugin-tsm": "^0.1.1",
"@web/parse5-utils": "^1.3.0",
"ast-types": "^0.14.2",
"boxen": "^6.2.1",
"ci-info": "^3.3.0",
"common-ancestor-path": "^1.0.1",
Expand Down Expand Up @@ -112,6 +113,7 @@
"preferred-pm": "^3.0.3",
"prismjs": "^1.27.0",
"prompts": "^2.4.2",
"recast": "^0.20.5",
"rehype-slug": "^5.0.1",
"resolve": "^1.22.0",
"rollup": "^2.70.1",
Expand Down
29 changes: 13 additions & 16 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { AddressInfo } from 'net';
import type * as babel from '@babel/core';
import type * as vite from 'vite';
import type { z } from 'zod';
import { z } from 'zod';
import type { AstroConfigSchema } from '../core/config';
import type { AstroComponentFactory, Metadata } from '../runtime/server';
import type { AstroRequest } from '../core/render/request';
Expand Down Expand Up @@ -60,11 +60,15 @@ export interface AstroGlobal extends AstroGlobalPartial {
}

export interface AstroGlobalPartial {
fetchContent<T = any>(globStr: string): Promise<FetchContentResult<T>[]>;
/**
* @deprecated since version 0.24. See the {@link https://astro.build/deprecated/resolve upgrade guide} for more details.
*/
resolve: (path: string) => string;
/** @deprecated Use `Astro.glob()` instead. */
fetchContent(globStr: string): Promise<any[]>;
glob(globStr: `${any}.astro`): Promise<ComponentInstance[]>;
glob<T extends Record<string, any>>(globStr: `${any}.md`): Promise<MarkdownInstance<T>[]>;
glob<T extends Record<string, any>>(globStr: string): Promise<T[]>;
site: URL;
}

Expand Down Expand Up @@ -508,20 +512,13 @@ export interface ComponentInstance {
getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult;
}

/**
* Astro.fetchContent() result
* Docs: https://docs.astro.build/reference/api-reference/#astrofetchcontent
*/
export type FetchContentResult<T> = FetchContentResultBase & T;

export type FetchContentResultBase = {
astro: {
headers: string[];
source: string;
html: string;
};
url: string;
};
export interface MarkdownInstance<T extends Record<string, any>> {
frontmatter: T;
file: string;
url: string | undefined;
Content: AstroComponentFactory;
getHeaders(): Promise<{ depth: number, slug: string, text: string }[]>;
}

export type GetHydrateCallback = () => Promise<(element: Element, innerHTML: string | null) => void>;

Expand Down
7 changes: 6 additions & 1 deletion packages/astro/src/core/render/dev/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ export function getStylesForURL(filePath: URL, viteServer: vite.ViteDevServer):
: // Otherwise, you are following an import in the module import tree.
// You are safe to use getModuleById() here because Vite has already
// resolved the correct `id` for you, by creating the import you followed here.
new Set([viteServer.moduleGraph.getModuleById(id)!]);
new Set([viteServer.moduleGraph.getModuleById(id)]);

// Collect all imported modules for the module(s).
for (const entry of moduleEntriesForId) {
// Handle this in case an module entries weren't found for ID
// This seems possible with some virtual IDs (ex: `astro:markdown/*.md`)
if (!entry) {
continue;
}
if (id === entry.id) {
scanned.add(id);
for (const importedModule of entry.importedModules) {
Expand Down
Loading

0 comments on commit 4299ab3

Please sign in to comment.