Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2 #223

Merged
merged 19 commits into from
Sep 3, 2024
Merged

v2 #223

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ public/pages/
.DS_Store
.vscode
*-lock.yaml

# RSS feed
app/api/blog/posts.json
app/api/blog/series.json
app/api/blog/rss.xml
app/api/blog/rss.br
4 changes: 4 additions & 0 deletions app.arc
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ prune true

@plugins
architect/plugin-bundles
enhance/arc-image-plugin
enhance/arc-plugin-enhance
enhance/arc-plugin-styles
create-post-metadata
create-series-metadata
create-rss-feed

@enhance-styles
filename css/styles.css
Expand Down
44 changes: 44 additions & 0 deletions app/api/blog/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import path from 'path'
import url from 'url'
import { readFileSync } from 'fs'
import { parseDate } from '../../lib/parseDate.mjs'
import navDataLoader from '../../docs/nav-data.mjs'

function isPublished (str) {
return process.env.DISABLE_DATE_CHECK === 'true'
? true
: parseDate(str) < Date.now()
}

/** @type {import('@enhance/types').EnhanceApiFn} */
export async function get (req) {
const account = req.session.account

let here = path.dirname(url.fileURLToPath(import.meta.url))
let base = path.join(here, 'posts.json')
let posts = JSON.parse(readFileSync(base, 'utf-8'))
.reverse()
.filter(({ frontmatter }) => isPublished(frontmatter.published))

const parsedLimit = parseInt(req.query.limit, 10)
const limit = parsedLimit > 0 ? parsedLimit : 20
const parsedOffset = parseInt(req.query.offset, 10)
const offset = parsedOffset >= 0 ? parsedOffset : 0
const total = posts.length

const { path: activePath } = req
const navData = navDataLoader('blog', activePath)

return {
json: {
posts,
pageTitle: 'Blog',
limit,
offset,
total,
account,
activeRoute: 'blog',
navData,
},
}
}
89 changes: 89 additions & 0 deletions app/api/blog/posts/$$.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import HljsLineWrapper from '../../../docs/hljs-line-wrapper.mjs'
import footnote from 'markdown-it-footnote'
import url from 'url'
import path from 'path'
import { Arcdown } from 'arcdown'
import { URL } from 'url'
import { default as defaultClassMapping } from '../../markdown-class-mappings.mjs'
// import { getWebMentions } from '../../../../shared/webmentions.mjs'
import { readFileSync } from 'fs'
import navDataLoader from '../../../docs/nav-data.mjs'

/** @type {import('@enhance/types').EnhanceApiFn} */
export async function get (req) {

// reinvoked each req so no weird regexp caching
const arcdown = new Arcdown({
plugins: {
footnote
},
pluginOverrides: {
markdownItToc: {
containerClass: 'toc mb2 ml-2',
listType: 'ul',
},
markdownItClass: defaultClassMapping,
},
hljs: {
sublanguages: { javascript: [ 'xml', 'css' ] },
plugins: [ new HljsLineWrapper({ className: 'code-line' }) ],
},
})

const account = req.session.account

const { path: activePath } = req
let docPath = activePath.replace(/^\/?blog\//, '') || 'index'
if (docPath.endsWith('/')) {
docPath += 'index' // trailing slash == index.md file
}

const docURL = new URL(`../../../blog/${docPath}.md`, import.meta.url)

let docMarkdown
try {
docMarkdown = readFileSync(docURL.pathname, 'utf-8')
}
catch (_err) {
return { statusCode: 404 }
}
const post = await arcdown.render(docMarkdown)
// const mentions = (await getWebMentions()).filter(mention => mention.targetPath === req.path && mention.approved)

let series = null
const { frontmatter } = post
const postSeries = frontmatter.series

if (postSeries) {
const here = path.dirname(url.fileURLToPath(import.meta.url))
const seriesJson = path.join(here, '..', 'series.json')
const seriesFile = readFileSync(seriesJson, 'utf-8')
const seriesData = JSON.parse(seriesFile)
series = seriesData[postSeries]
}

let cacheControl = process.env.ARC_ENV === 'production'
? 'max-age=3600;'
: 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0'

const navData = navDataLoader('blog', activePath)

return {
headers: {
'cache-control': cacheControl,
},
json: {
post,
pageTitle: `${post.frontmatter.title.replaceAll(/"/g, "'")}`,
pageDescription: post.frontmatter.description
? post.frontmatter.description.replaceAll(/"/g, "'")
: undefined,
pageImage: post.frontmatter.image,
series,
// mentions,
account,
activeRoute: 'blog',
navData,
},
}
}
31 changes: 31 additions & 0 deletions app/api/blog/rss.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import path from 'path'
import url from 'url'
import { readFileSync } from 'fs'

export async function get (req) {
let here = path.dirname(url.fileURLToPath(import.meta.url))

let acceptEncoding = (req.headers && req.headers['accept-encoding'] ||
req.headers && req.headers['Accept-Encoding'])
let returnCompressed = acceptEncoding && acceptEncoding.includes('br')

let resp = {
statusCode: 200,
headers: {
'content-type': 'application/rss+xml; charset=UTF-8',
},
}

if (returnCompressed) {
let postsFilePath = path.join(here, 'rss.br')
resp.body = readFileSync(postsFilePath, 'utf-8')
resp.isBase64Encoded = true
resp.headers['content-encoding'] = 'br'
}
else {
let postsFilePath = path.join(here, 'rss.xml')
resp.body = readFileSync(postsFilePath, 'utf-8')
}

return resp
}
1 change: 1 addition & 0 deletions app/api/cookbook/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export async function get (req) {
'cache-control': cacheControl,
},
json: {
pageTitle: 'Cookbook',
navData,
},
}
Expand Down
34 changes: 34 additions & 0 deletions app/api/markdown-class-mappings.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export default {
h1: [ 'font-bold', 'mb0', 'text3', 'text4-lg', 'leading2' ],
h2: [ 'font-bold', 'mbe0', 'mbs4', 'text1', 'text3-lg', 'leading2' ],
h3: [ 'font-bold', 'mbe0', 'mbs4', 'text0', 'text2-lg', 'leading2' ],
h4: [ 'font-bold', 'mbe0', 'mbs4', 'text0', 'leading2' ],
p: [ 'mbs-1', 'mbe0' ],
ol: [ 'mb2', 'mis2' ],
ul: [ 'mb2', 'mis2' ],
blockquote: [ 'mb2', 'p0' ],
strong: [ 'font-bold' ],
table: [
'md-table',
'table-fixed',
'si-100',
'mbe1',
'text-1',
'text0-lg',
'border-collapse',
'border-spacing',
],
th: [
'border-solid',
'border1',
'word-wrap',
'md-table-padding',
],
td: [
'border-solid',
'border1',
'word-wrap',
'md-table-padding',
],
img: [ 'pb0', 'block', 'mi-auto' ],
}
1 change: 1 addition & 0 deletions app/api/wasm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export async function get ({ query }) {
'cache-control': cacheControl,
},
json: {
pageTitle: 'Enhance WASM',
thanks,
navData,
},
Expand Down
1 change: 1 addition & 0 deletions app/api/why-enhance.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export async function get () {
'cache-control': cacheControl,
},
json: {
pageTitle: 'Why Enhance?',
navData,
},
}
Expand Down
52 changes: 52 additions & 0 deletions app/blog/posts/2022-08-31-new-begin-and-enhance-html-framework.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: "Announcing a whole new Begin, and our new HTML framework, Enhance"
image: '/_public/blog/post-assets/arc58.png'
description: A whole new Begin is coming, designed from the ground up alongside our all-new standards-based HTML framework, Enhance.
author: 'Brian LeRoux'
avatar: 'brian.jpg'
twitter: "brianleroux"
published: 'August 31, 2022'
---

![Begin](/_public/blog/post-assets/arc58.png)

tl;dr: a whole new Begin is coming, designed from the ground up alongside [Enhance](https://enhance.dev), our all-new standards-based HTML framework.

[Enhance is available today](https://enhance.dev) for everyone to use (and does not require Begin). Together, we hope Begin + Enhance will help transform the way frontend web devs and designers build full-stack web applications.

The next Begin will be shipping soon, [sign up for updates here](https://docs.google.com/forms/d/e/1FAIpQLScPxdqdFI2zkNZ3syCze_Mgj2ba3ZzHWQawKJbFukqFMVrXsA/viewform)!

Existing Begin users: in addition to getting priority access to the new Begin, we'll also support your current Begin apps' CI/CD workflows in perpetuity. We value your trust, and we respect that stability is essential to our customers.

---

### Background <!-- remove me when the HR displays -->

In late 2019 we launched Begin: a [Functional Web App (FWA)](https://fwa.dev) platform designed to help ship low code serverless web apps. With tens of thousands of applications comfortably humming along today, we're stoked to announce a new platform version that incorporates all your feedback, and everything we've learned about building FWAs over the last two years.

While folks love building FWAs (and ever-increasing numbers of you are doing so), developers frequently struggle with the modern JavaScript ecosystem, especially in frontend development. Moreover, Begin primarily left it up to you to decide (or figure out) how best to build the frontend of your FWA.

We heard you loud and clear: we abdicated our responsibility to that critical area of the web dev stack. So last year we began building what eventually became [Enhance](https://enhance.dev), which we are very excited to introduce today.


## Begin: Enhanced

![Axol](/_public/blog/post-assets/axol-cloud.jpg)

Modern JavaScript frameworks often lock you into niche ecosystems with custom dialects, and forcing through a revolving door of breaking changes. We've seen the thrash – and the broken builds – these frameworks cause. We're fed up with it, and we know you are too.

So we're taking a stand and fixing these brittle libraries with our very best tool: web standards.

The web is an incredibly durable medium. While frameworks frequently break, web browsers must remain completely stable and backward compatible for years, even decades. That's why Enhance's web standards-based approach provides you a dependable foundation for creating lightweight, flexible, and future-proof web applications.

The Enhance ethos is to incrementally build in plain HTML, then progressively *enhance* your way to a production-ready web app. ([Read more about our core philosophy here](https://enhance.dev/docs/learn/why-enhance).)

Starting with Enhance yields an HTML-centered project complete with file-based routing, reusable Custom Elements, a customizable utility CSS system, and mapped API data routes that get deployed to isolated, single-purpose cloud functions. It's a complete frontend development solution based entirely on fast, stable, tried-and-true web standards.

Alongside Enhance, we're also announcing an all-new version of the Begin platform. The new Begin will be built from the ground up to take full advantage of Enhance, and incorporates our opinions – and your feedback – on the best approaches to building modern, stable software for the web.

Existing Begin customers will get priority (in fact, you're already on the waitlist), and [devs new to Begin can sign up here](https://docs.google.com/forms/d/e/1FAIpQLScPxdqdFI2zkNZ3syCze_Mgj2ba3ZzHWQawKJbFukqFMVrXsA/viewform).

Thank you! We truly appreciate your support and look forward to this next chapter! If you have any questions or concerns, please feel free to contact me directly: brian@begin.com

## Now go [try out Enhance](https://enhance.dev) and [sign up for Begin](https://docs.google.com/forms/d/e/1FAIpQLScPxdqdFI2zkNZ3syCze_Mgj2ba3ZzHWQawKJbFukqFMVrXsA/viewform)!
81 changes: 81 additions & 0 deletions app/blog/posts/2022-09-06-enhancing-todomvc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
title: "Enhancing TodoMVC"
image: '/_public/blog/post-assets/todos.jpg'
category: uncategorized
description: Porting the venerable TodoMVC app to Enhance. In this post we'll build a todo app without any client-side JavaScript.
author: 'Simon MacDonald'
avatar: 'simon.png'
twitter: "macdonst"
published: 'September 6, 2022'
---

![todos](/_public/blog/post-assets/todos.jpg)
<small>Photo by <a href="https://unsplash.com/@glenncarstenspeters?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Glenn Carstens-Peters</a> on <a href="https://unsplash.com/s/photos/todo?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></small>

Last week at [CascadiaJS](https://2022.cascadiajs.com/), [Begin](https://begin.com/) [announced](https://blog.begin.com/posts/2022-08-31-new-begin-and-enhance-html-framework) our new HTML first framework, [Enhance](https://enhance.dev/docs/). It wasn’t long before folks asked about building a Todo app with Enhance on our [Discord](https://discord.gg/UWauMMtt).

![any_plans](/_public/blog/post-assets/any-plans.png)

So I thought it would be interesting to take a pass at building the venerable [TodoMVC](https://todomvc.com/) example as an Enhance app. Our version will use Web Components server-side rendered by Enhance with no client-side JavaScript.

## TL;DR

If you just want to jump right into the app, you can [clone the repository](https://github.com/macdonst/enhance-todomvc) and run the following commands from your terminal:

```bash
npm install
npm start
```

Once the app starts up, navigate to [http://localhost:3333/todos](http://localhost:3333/todos).

![demo](/_public/blog/post-assets/todos-demo.gif)

## Application structure

Enhance apps are a combination of APIs, web components and pages. Our version of TodoMVC should look like this when you clone it:

```
app
├── api
| ├── todos
| | └── $id
| | | └── delete.mjs
| | ├── $id.mjs
| | ├── clearcompleted.mjs
| | └── toggleall.mjs
| └── todos.mjs
├── elements
| ├── todo-footer.mjs
| ├── todo-item.mjs
| └── todo-main.mjs
└── pages
| └── todos.mjs
├── head.mjs
models
├── schemas
| └── todos.mjs
└── todos.mjs
```

## How does it all work?

Great question. Let’s trace what happens when you hit the route [http://localhost:3333/todos](http://localhost:3333/todos).

1. The browser sends a GET request to `/todos`.
2. The Enhance application checks to see if there is an API route that matches `/todos`.
3. In this case, the `get` method of `api/todos.mjs` is executed.
4. The `get` method queries DynamoDB to get all the saved todos and returns a JSON payload.
5. The application then calls the `pages/todos.mjs`, including the JSON payload from step four as part of our [store](https://enhance.dev/docs/learn/concepts/state/store). The store will be passed down to all the web components loaded by this page.
6. Our `pages/todos.mjs` renders out HTML by server-side rendering the apps web components, which you can find in the `elements` folder.

The app is entirely server-side rendered and has no client-side JavaScript. Go ahead and disable JavaScript in your browser (instructions for [Chrome](https://developer.chrome.com/docs/devtools/javascript/disable/), [Edge](https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide-chromium/javascript/disable), [Firefox](https://www.lifewire.com/disable-javascript-in-firefox-446039) and [Safari](https://www.lifewire.com/disable-javascript-in-safari-4103708)), and you’ll notice the app still works! It’s not magic. It’s just the judicious use of the `<form>` element.

## Where to take things from here?

Is this exactly how we’d build an Enhance app from scratch? Well no. This is a quick port of the existing TodoMVC app to Enhance. Join us next week, where we’ll show you how to progressively enhance the application with a sprinkling of JavaScript while retaining this baseline of functionality without JavaScript.

## Next Steps

1. Try out the [Enhance Quickstart!](https://enhance.dev/docs/)
2. [Sign up](https://docs.google.com/forms/d/e/1FAIpQLScPxdqdFI2zkNZ3syCze_Mgj2ba3ZzHWQawKJbFukqFMVrXsA/viewform) for early access to the next version of Begin
Loading
Loading