Skip to content

Commit f8853b9

Browse files
committed
Change imports and add static-blog-from-markdown-files
1 parent 1a43a65 commit f8853b9

File tree

3 files changed

+78
-66
lines changed

3 files changed

+78
-66
lines changed

astro.config.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { defineConfig } from 'astro/config';
33
import starlight from '@astrojs/starlight';
44

55
export const guideChapters = [
6-
{ label: 'Why learn HTML and CSS?', slug: 'guides/why-html-css' },
6+
{ label: 'Motivation: Why learn HTML and CSS?', slug: 'guides/why-html-css' },
77
{ label: 'About JavaScript', slug: 'guides/about-javascript' },
88
{ label: 'Setup: GitHub and VS Code for the Web', slug: 'guides/setup' },
99
{ label: 'Start with HTML', slug: 'guides/html' },
1010
{ label: 'Style with CSS', slug: 'guides/css' },
1111
{ label: 'JavaScript for pages with shared components', slug: 'guides/javascript-to-render-multiple-pages-with-shared-components' },
12-
// { label: 'A static blog from markdown files', slug: 'guides/static-blog-from-markdown-files' },
12+
{ label: 'A static blog from markdown files', slug: 'guides/static-blog-from-markdown-files' },
1313
]
1414

1515
// https://astro.build/config

src/content/docs/guides/javascript-to-render-multiple-pages-with-shared-components.md

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ So far your website still consists of only a single page: the home page. Add a s
106106
2. Move the `<header>` and its contents to a new file `components/Header.js` and wrap it in a bit of JavaScript:
107107

108108
```js title=components/Header.js
109-
import { html } from 'mastro/html.js';
109+
import { html } from "mastro";
110110

111111
export const Header = () =>
112112
html`
@@ -126,7 +126,7 @@ There are a few things going on here:
126126
Analogous to `Header.js`, create a second file:
127127

128128
```js title=components/Footer.js
129-
import { html } from 'mastro/html.js';
129+
import { html } from "mastro";
130130

131131
export const Footer = () =>
132132
html`
@@ -149,18 +149,17 @@ Now, to `import` the two functions we just created, you first need to convert th
149149
Rename the `routes/index.html` file to `routes/index.server.js` and change its contents to:
150150

151151
```js title=routes/index.server.js ins={1-8,15,22,25}
152-
import { html } from 'mastro/html.js';
153-
import { htmlToResponse } from 'mastro/routes.js';
154-
import { Header } from '../components/Header.js';
155-
import { Footer } from '../components/Footer.js';
152+
import { html, htmlToResponse } from "mastro";
153+
import { Header } from "../components/Header.js";
154+
import { Footer } from "../components/Footer.js";
156155

157156
export const GET = () =>
158157
htmlToResponse(
159158
html`
160159
<html>
161160
<head>
162161
<title>My website</title>
163-
<link rel="stylesheet" href="styles.css">
162+
<link rel="stylesheet" href="/styles.css">
164163
</head>
165164
<body>
166165
${Header()}
@@ -193,16 +192,16 @@ Load the page in the Mastro preview to see whether it still works!
193192
Now you're almost ready to create that second page. Just one more thing to move to its own component file, because we want to reuse it: the skeleton of the page, often called `Layout`. Create a new file:
194193

195194
```js title=components/Layout.js
196-
import { html } from 'mastro/html.js';
197-
import { Header } from './Header.js';
198-
import { Footer } from './Footer.js';
195+
import { html } from "mastro";
196+
import { Header } from "./Header.js";
197+
import { Footer } from "./Footer.js";
199198

200199
export const Layout = (props) =>
201200
html`
202201
<html>
203202
<head>
204203
<title>${props.title}</title>
205-
<link rel="stylesheet" href="styles.css">
204+
<link rel="stylesheet" href="/styles.css">
206205
</head>
207206
<body>
208207
${Header()}
@@ -222,9 +221,8 @@ The above component is still just a function, but a function that takes one argu
222221
Now you can reduce your `routes/index.server.js` file to:
223222

224223
```js title=routes/index.server.js
225-
import { html } from 'mastro/html.js';
226-
import { htmlToResponse } from 'mastro/routes.js';
227-
import { Layout } from '../components/Layout.js';
224+
import { html, htmlToResponse } from "mastro";
225+
import { Layout } from "../components/Layout.js";
228226

229227
export const GET = () =>
230228
htmlToResponse(
@@ -243,9 +241,8 @@ Note how we pass an object of the form `{ title, children }` as an argument to t
243241
Now finally all that work pays off: add that second page by creating a new file:
244242

245243
```js title=routes/news.server.js
246-
import { html } from 'mastro/html.js';
247-
import { htmlToResponse } from 'mastro/routes.js';
248-
import { Layout } from '../components/Layout.js';
244+
import { html, htmlToResponse } from "mastro";
245+
import { Layout } from "../components/Layout.js";
249246

250247
export const GET = () =>
251248
htmlToResponse(

src/content/docs/guides/static-blog-from-markdown-files.md

Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -47,81 +47,96 @@ date: 2024-01-31
4747
This is our second blog post.
4848
```
4949

50-
To list all your blog posts, change `routes/news.server.js` to:
50+
## The index page
5151

52-
```js title=routes/news.server.js
53-
import { html } from 'mastro/html.js';
54-
import { readMarkdownFiles } from 'mastro/markdown.js';
55-
import { htmlToResponse } from 'mastro/routes.js';
56-
import { Layout } from '../components/Layout.js';
52+
Instead of changing our `routes/news.server.js` file in-place, first move it to a new folder `routes/news/` and rename it to `index.server.js`. This doesn't change anything, but we'll need the folder later for the detail pages anyway.
53+
54+
To list all your blog posts, change the index page to:
55+
56+
```js title=routes/news/index.server.js
57+
import { html, htmlToResponse, readMarkdownFiles } from "mastro";
58+
import { Layout } from "../../components/Layout.js";
5759

5860
export const GET = async () => {
59-
const posts = await readMarkdownFiles('data/posts/*.md');
61+
const posts = await readMarkdownFiles("data/posts/*.md");
6062
return htmlToResponse(
6163
Layout({
62-
title: 'News',
63-
children: posts.map(post =>
64-
html`<p><a href=${post.path}>${post.meta.title}</a></p>`
65-
)
66-
})
64+
title: "News",
65+
children: posts.map((post) =>
66+
html`
67+
<p>
68+
<a href="${"/news" + post.path.slice(11, -3)}">
69+
${post.meta.title}
70+
</a>
71+
</p>
72+
`
73+
),
74+
}),
6775
);
68-
}
76+
};
6977
```
7078

7179
Note the use of the `readMarkdownFile` function that we imported from mastro. Because it needs to read the files from disk, it's an `async` function. That's why we need to `await` it, which in turn forces us to mark up our `GET` function as `async` as well.
7280

73-
Have a look at `/news` in the Mastro preview pane. Clicking one of the links will lead you to a page saying "404, page not found". That's because those pages don't exist yet. Create them by creating the file `routes/news/[slug].server.js`.
81+
The `.slice(11, -3)` cuts off the first eleven and the last three character from the string: in our case it removes the leading `/data/posts` and the trailing `.md` from the filename.
82+
83+
Have a look at `/news` in the Mastro preview pane. Clicking one of the links will lead you to a page saying "404, page not found". That's because those pages don't exist yet.
7484

75-
The `[slug]` is a parameter. When your server receives a request for `/news/2024-01-30-hello-world`, the request will be routed to the `news/[slug].server.js` page and the `context.params.slug` variable will hold the value `2024-01-30-hello-world`.
7685

77-
```js title=routey/news/[slug].server.js
78-
import { html } from 'mastro/html.js';
79-
import { readMarkdownFile } from 'mastro/markdown.js';
80-
import { htmlToResponse } from 'mastro/routes.js';
81-
import { Layout } from '../components/Layout.js';
86+
## Detail pages
8287

83-
export const GET = async (req, context) => {
84-
const post = await readMarkdownFile('data/posts/' + context.params.slug + '.md')
88+
Create the detail pages for the individual blog posts by creating the file `routes/news/[slug].server.js`.
89+
90+
The `[slug]` is a parameter. When your server receives an HTTP request for `/news/2024-01-30-hello-world`, the request will be routed to the `news/[slug].server.js` file. To read out the parameter, we use the `Request` object that is passed into our `GET` function, and the `getParams` helper function:
91+
92+
```js title=routes/news/[slug].server.js
93+
import { getParams, htmlToResponse, readMarkdownFile } from "mastro";
94+
import { Layout } from "../../components/Layout.js";
95+
96+
export const GET = async (req) => {
97+
const { slug } = getParams(req.url);
98+
const post = await readMarkdownFile(`data/posts/${slug}.md`);
8599
return htmlToResponse(
86100
Layout({
87101
title: post.meta.title,
88-
children: post.body,
102+
children: post.content,
89103
})
90104
);
91105
}
92106
```
93107

108+
To read the markdown file from disk and convert the markdown to HTML, we use the asynchronous `readMarkdownFile` function.
109+
94110
Test following the links in the Mastro preview pane now. Congratulations, you have a working blog!
95111

96112

97-
## Generating parametrized pages
113+
## Generating the route parameters
98114

99-
To pre-generate all your html files, run `deno task build`. It will tell you that it generated the `out/index.html` page, but that `routes/news/[slug].server.js` is missing a `staticParams` field. That's because mastro can't magically guess all the blog post urls that we want to generate.
115+
Try generating all your HTML files: click the **Generate** button in the top-right corner of the Mastro preview pane.
100116

101-
To let it know, we import and use the `htmlRoute` function, which takes two arguments:
117+
It will generate the pages without parameters. But notice the error telling you that "/routes/news/[slug].server.js should export a function named getStaticPaths".
102118

103-
1. a configuration object (with the slugs to put into the paths to pre-generate), and
104-
2. the function which we already had previously.
119+
That's because Mastro cannot guess the paths for all the pages that we want to generate. In the preview (or on a running server) this works because the information is provided directly in the URL. But if we want to statically generate all the pages ahead of time, we need to tell Astro the paths of all our pages with route parameters. We do that by exporting a function called `getStaticPaths`, that returns an array of strings when called.
105120

106-
Change `routes/news/[slug].server.js` to:
121+
```js title=routes/news/[slug].server.js ins={1,15-18}
122+
import { getParams, htmlToResponse, readDir, readMarkdownFile } from "mastro";
123+
import { Layout } from '../../components/Layout.js';
107124

108-
```js title=routey/news/[slug].server.js
109-
import { htmlRoute, readMdFile, readMdFiles } from 'mastro'
110-
import { Layout } from '../components/Layout.js'
125+
export const GET = async (req) => {
126+
const { slug } = getParams(req.url);
127+
const post = await readMarkdownFile(`data/posts/${slug}.md`);
128+
return htmlToResponse(
129+
Layout({
130+
title: post.meta.title,
131+
children: post.content,
132+
})
133+
);
134+
}
111135

112-
export const GET = htmlRoute(
113-
{
114-
staticParams: await readMarkdownFile('data/posts/*.md').then(post => ({ slug: post.slug }))
115-
},
116-
async (req, params) => {
117-
const post = await readMarkdownFile('data/posts/' + params.slug + '.md')
118-
const { title } = post.data
119-
return (
120-
<Layout title={title}>
121-
<h1>{title}</h1>
122-
{post.body}
123-
</Layout>
124-
)
125-
}
126-
)
136+
export const getStaticPaths = async () => {
137+
const posts = await readDir("data/posts/");
138+
return posts.map(p => "/news/" + p.slice(0, -3));
139+
}
127140
```
141+
142+
Now click **Generate** again. Then don't forget to [save your changes in the _Source Control_ tab](/guides/html/#save-changes-and-publish-to-the-web). Congratulations to your live blog!

0 commit comments

Comments
 (0)