diff --git a/app/api/cookbook/$$.mjs b/app/api/cookbook/$$.mjs
index 34cb3600..d91bf6c9 100644
--- a/app/api/cookbook/$$.mjs
+++ b/app/api/cookbook/$$.mjs
@@ -1,21 +1,64 @@
-import navDataLoader from '../../docs/nav-data.mjs'
+/* eslint-disable filenames/match-regex */
+import { readFileSync } from 'fs'
+import { URL } from 'url'
+import { Arcdown } from 'arcdown'
+import arcStaticImg from 'markdown-it-arc-static-img'
+import navDataLoader, {
+ other as otherLinks,
+} from '../../docs/nav-data.mjs'
+import HljsLineWrapper from '../../docs/hljs-line-wrapper.mjs'
-export async function get (req) {
- const { path: activePath } = req
+const arcdown = new Arcdown({
+ pluginOverrides: {
+ markdownItToc: {
+ containerClass: 'toc mbe2 mis-2 leading2',
+ listType: 'ul',
+ level: [ 1, 2, 3 ],
+ },
+ },
+ plugins: [ arcStaticImg ],
+ hljs: {
+ sublanguages: { javascript: [ 'xml', 'css' ] },
+ plugins: [ new HljsLineWrapper({ className: 'code-line' }) ],
+ },
+})
- const cacheControl =
- process.env.ARC_ENV === 'production'
- ? 'max-age=3600'
- : 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0'
+/** @type {import('@enhance/types').EnhanceApiFn} */
+export async function get (request) {
+ const { path: activePath } = request
+ let recipePath = activePath.replace(/^\/?docs\//, '') || 'index'
+
+ let recipeURL = new URL(`../../${recipePath}.md`, import.meta.url)
const navData = navDataLoader('docs', activePath)
+ let recipeMarkdown
+ try {
+ recipeMarkdown = readFileSync(recipeURL.pathname, 'utf-8')
+ }
+ catch (e) {
+ return {
+ location: '/404'
+ }
+ }
+
+ const recipe = await arcdown.render(recipeMarkdown)
+
+ const initialState = {
+ recipe,
+ otherLinks,
+ navData,
+ }
+
+ let cacheControl =
+ process.env.ARC_ENV === 'production'
+ ? 'max-age=3600;'
+ : 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0'
+
return {
headers: {
'cache-control': cacheControl,
},
- json: {
- navData,
- },
+ json: initialState,
}
}
diff --git a/app/api/cookbook/index.mjs b/app/api/cookbook/index.mjs
new file mode 100644
index 00000000..34cb3600
--- /dev/null
+++ b/app/api/cookbook/index.mjs
@@ -0,0 +1,21 @@
+import navDataLoader from '../../docs/nav-data.mjs'
+
+export async function get (req) {
+ const { path: activePath } = req
+
+ const cacheControl =
+ process.env.ARC_ENV === 'production'
+ ? 'max-age=3600'
+ : 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0'
+
+ const navData = navDataLoader('docs', activePath)
+
+ return {
+ headers: {
+ 'cache-control': cacheControl,
+ },
+ json: {
+ navData,
+ },
+ }
+}
diff --git a/app/docs/md/patterns/building-for-the-browser.md b/app/cookbook/build-for-the-browser.md
similarity index 99%
rename from app/docs/md/patterns/building-for-the-browser.md
rename to app/cookbook/build-for-the-browser.md
index 4afb9c9c..8341e852 100644
--- a/app/docs/md/patterns/building-for-the-browser.md
+++ b/app/cookbook/build-for-the-browser.md
@@ -1,5 +1,5 @@
---
-title: Building for the browser
+title: Build for the browser
---
## The `@bundles` plugin
diff --git a/app/docs/md/patterns/architect-migration.md b/app/cookbook/migrate-from-architect.md
similarity index 99%
rename from app/docs/md/patterns/architect-migration.md
rename to app/cookbook/migrate-from-architect.md
index 22173e78..347e128b 100644
--- a/app/docs/md/patterns/architect-migration.md
+++ b/app/cookbook/migrate-from-architect.md
@@ -1,7 +1,5 @@
---
-title: Architect Migration
-links:
- - "arc.codes": https://arc.codes
+title: Migrate from Architect
---
Enhance uses [Architect](https://arc.codes) under the hood for local development and deployment. It is possible to migrate between a typical Architect project structure and the Enhance file-based routing. It is also possible to mix the two approaches together in the same app. They are incrementally adoptable in both directions.
diff --git a/app/docs/md/patterns/rendering-markdown.md b/app/cookbook/render-markdown.md
similarity index 98%
rename from app/docs/md/patterns/rendering-markdown.md
rename to app/cookbook/render-markdown.md
index 03b3084d..e030e163 100644
--- a/app/docs/md/patterns/rendering-markdown.md
+++ b/app/cookbook/render-markdown.md
@@ -1,7 +1,5 @@
---
-title: Rendering Markdown
-links:
- - "Arcdown": https://github.com/architect/arcdown/blob/main/readme.md
+title: Render Markdown
---
Enhance can be used to render Markdown with minimal effort — in fact, this very site is itself an Enhance app that renders Markdown to HTML on demand. You can dig into the [source code](https://github.com/enhance-dev/enhance.dev) to see exactly how we've set it up, or follow along below.
diff --git a/app/cookbook/roll-your-own-auth.md b/app/cookbook/roll-your-own-auth.md
new file mode 100644
index 00000000..d0d42086
--- /dev/null
+++ b/app/cookbook/roll-your-own-auth.md
@@ -0,0 +1,7 @@
+---
+title: Roll your own auth
+---
+
+In this five part series on the Begin blog, you’ll learn what authentication is, how it works, and how to implement it yourself. We cover username and password flows, email and phone verification, using magic links, and interfacing with OAuth.
+
+**[Get started on the Begin blog](https://begin.com/blog/posts/2023-05-10-why-you-should-roll-your-own-auth)!**
diff --git a/app/docs/md/patterns/event-listeners.md b/app/cookbook/use-event-listeners.md
similarity index 99%
rename from app/docs/md/patterns/event-listeners.md
rename to app/cookbook/use-event-listeners.md
index d93ce45b..6882c5f9 100644
--- a/app/docs/md/patterns/event-listeners.md
+++ b/app/cookbook/use-event-listeners.md
@@ -1,5 +1,5 @@
---
-title: Event Listeners
+title: Use event listeners
---
An event is a signal that something has happened on your page. The browser notifies you so you can react to them.
diff --git a/app/docs/md/patterns/form-validation.md b/app/cookbook/validate-forms.md
similarity index 99%
rename from app/docs/md/patterns/form-validation.md
rename to app/cookbook/validate-forms.md
index f101a51e..835b85dd 100644
--- a/app/docs/md/patterns/form-validation.md
+++ b/app/cookbook/validate-forms.md
@@ -1,5 +1,5 @@
---
-title: Form Validation
+title: Validate forms
---
HTML forms are very powerful on their own.
diff --git a/app/docs/md/patterns/testing/index.md b/app/cookbook/write-unit-tests.md
similarity index 98%
rename from app/docs/md/patterns/testing/index.md
rename to app/cookbook/write-unit-tests.md
index 05c7e11a..3d67bff6 100644
--- a/app/docs/md/patterns/testing/index.md
+++ b/app/cookbook/write-unit-tests.md
@@ -1,5 +1,5 @@
---
-title: Testing
+title: Write unit tests
---
A big benefit of Enhance custom element [pure functions](https://en.wikipedia.org/wiki/Pure_function) is that they return a string that you can test against an expected output. It doesn't need to get any more complicated than that to get started.
diff --git a/app/elements/cookbook/article.mjs b/app/elements/cookbook/article.mjs
new file mode 100644
index 00000000..852ca7be
--- /dev/null
+++ b/app/elements/cookbook/article.mjs
@@ -0,0 +1,221 @@
+export default function CookbookArticle ({ html }) {
+ return html`
+
+
+ Learning new things can be fun — but also challenging. The Enhance Cookbook is here to show you around the kitchen and help you get your hands dirty. +
++ Use Arcdown to render Markdown content into your Enhance app. +
++ Use DOM events to respond to dynamic user input. +
++ Improve UX and prevent errors by validating forms on the client and the server. +
++ Learn how to implement authentication, securely and effectively. +
++ Ship and run code on the browser within a server side rendered Enhance app. +
++ Test Enhance elements and API routes. +
++ Learn how to migrate your Architect app to an Enhance app. +
+${nextLink.description}
` : '' } @@ -25,20 +24,18 @@ function communityResources (communityLinks) { const description = link?.description || '' return /* html */ ` -${description} -