Skip to content

Commit

Permalink
add byline to website
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Kim authored and Brian Kim committed Nov 7, 2023
1 parent 78a3120 commit 7dfea2b
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 10 deletions.
4 changes: 3 additions & 1 deletion website/documents/blog/2020-04-15-introducing-crank.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
title: Introducing Crank
publishDate: 2020-04-15
author: Brian Kim
authorURL: https://github.com/brainkim
---

After months of development, I’m happy to introduce Crank.js, a new framework for creating JSX-driven components with functions, promises and generators. And I know what you’re thinking: *oh no, not another web framework.* There are already so many of them out there and each carries a non-negligible cost in terms of learning it and building an ecosystem to surround it, so it makes sense that you would reject newcomers if only to avoid the deep sense of exhaustion which has come to be known amongst front-end developers as “JavaScript fatigue.” Therefore, this post is both an introduction to Crank as well as an apology: I’m sorry for creating yet another framework, and I hope that by explaining the circumstances which led me to do so, you will forgive me.
Expand Down Expand Up @@ -75,7 +77,7 @@ This is some pseudo-code I sketched out, where the calls to `componentDidWhat()`

Furthermore, you could implement something like the `componentDidCatch()` and `componentWillUnmount()` lifecycle methods directly within the async generator, by wrapping the `yield` operator in a `try`/`catch`/`finally` block. And the framework could, upon producing DOM nodes, pass these nodes back into the generator, so you could do direct DOM manipulations without React’s notion of “refs.” All these things which React required separate methods or hooks to accomplish could be done within async generator functions with just the control-flow operators that JavaScript provides, and all within the same scope.

This idea didn’t come all at once, but it dazzled me nonetheless, and for the first time I felt like the task of creating a framework was achievable. I didn’t know all the details behind how to implement the API above, and I still didn’t know how the framework would handle async functions or sync generator functions, but I saw the start and the end, something to motivate me when I got stuck. And the best part was that it felt like “innovation arbitrage,” where, while the React team spent its considerable engineering talent on creating a “UI runtime,” I could just delegate the hard stuff to JavaScript. I didn’t need to flatten call stacks into a “fiber” data structure so that computations could be arbitrarily paused and resumed; rather, I could just let the `await` and `yield` operators do the suspending and resuming for me. And I didn’t need to create a scheduler like the React team is doing; rather, I could use promises and the microtask queue to coordinate asynchrony between components. For all the hard things that the React team was doing, a solution seemed latent within the JavaScript runtime. I just had to apply it.
This idea didn’t come all at once, but it dazzled me nonetheless, and for the first time I felt like the task of creating a framework was achievable. I didn’t know all the details behind how to implement the API above, and I still didn’t know how the framework would handle async functions or sync generator functions, but I saw the start and the end, something to motivate me when I got stuck. And the best part was that it felt like “innovation arbitrage,” where, while the React team spent its considerable engineering talent on creating a “UI runtime,” I could just delegate the hard stuff to JavaScript. I didn’t need to flatten call stacks into a “fiber” data structure so that computations could be arbitrarily paused and resumed; rather, I could just let the `await` and `yield` operators do the suspending and resuming for me. And I didn’t need to create a scheduler like the React team was doing; rather, I could use promises and the microtask queue to coordinate asynchrony between components. For all the hard things that the React team was doing, a solution seemed latent within the JavaScript runtime. I just had to apply it.

## Not Just Another Web Framework
Crank is the result of a months-long investigation into the viability of this idea, that JSX-based components could be written not just with sync functions, but also with async functions, and with sync and async generator functions. Much of this time was spent refining the design of the API, figuring out what to do, for instance, when an async component is still pending but rerendered, and how things like event handling should work. As it turns out, the simplicity of directly awaiting promises within your components is unmatched by any API the React team has put out, and sync generator functions turned out to be just as if not more useful than async generator functions. I’m very pleased with the result. I literally started tearing up while implementing TodoMVC in Crank, partly because it was the culmination of months of work, but also because it felt so natural and easy.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
title: Writing Crank from Scratch
publishDate: 2020-10-13
author: Brian Kim
authorURL: https://github.com/brainkim
---

One of my goals when authoring Crank.js was to create a framework which was so simple that any intermediate JavaScript developer could conceivably write it from scratch. What I think makes this uniquely achievable for Crank is that its component model is built on top of JavaScript’s two main control flow abstractions, iterators and promises, allowing developers to write components exclusively with sync and async functions and generator functions.
Expand Down
9 changes: 7 additions & 2 deletions website/src/components/blog-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import type {Children} from "@b9g/crank";

export interface BlogContentProps {
title: string;
author?: string;
authorURL?: string;
publishDate?: Date;
children: Children;
}

export function BlogContent({title, publishDate, children}: BlogContentProps) {
export function BlogContent({title, publishDate, author, authorURL, children}: BlogContentProps) {
const publishDateDisplay =
publishDate &&
publishDate.toLocaleString("en-US", {
Expand All @@ -19,7 +21,10 @@ export function BlogContent({title, publishDate, children}: BlogContentProps) {

return jsx`
<h1>${title}</h1>
${publishDateDisplay && jsx`<p>${publishDateDisplay}</p>`}
<p>
${author && jsx`By <a href=${authorURL} rel="author">${author}</a>`} \
${publishDateDisplay && jsx`<span>– Published ${publishDateDisplay}</span>`}
</p>
${children}
`;
}
11 changes: 8 additions & 3 deletions website/src/views/blog-home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,17 @@ export default async function BlogHome({context: {storage}}: ViewProps) {
}
}
const {title, publishDate} = post.attributes;
const {title, publishDate, author, authorURL} = post.attributes;
return jsx`
<div class="content">
<${BlogContent} title=${title} publishDate=${publishDate}>
<${BlogContent}
title=${title}
publishDate=${publishDate}
author=${author}
authorURL=${authorURL}
>
<${Marked} markdown=${body} components=${components} />
<//BlogContent>
<//BlogContent>
<div>
<a href=${post.url}>Read more…</a>
</div>
Expand Down
12 changes: 8 additions & 4 deletions website/src/views/blog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {jsx} from "@b9g/crank/standalone";
import * as path from "path";

import {Root} from "../components/root.js";
import {Main, Sidebar} from "../components/sidebar.js";
import {Main} from "../components/sidebar.js";
import {BlogContent} from "../components/blog-content.js";
import {Marked} from "../components/marked.js";
import {components} from "../components/marked-components.js";
Expand All @@ -25,14 +25,18 @@ export default async function BlogPage({url, context: {storage}}: ViewProps) {
}

const {
attributes: {title, publishDate},
attributes: {title, publishDate, author, authorURL},
body,
} = post;
return jsx`
<${Root} title="Crank.js | ${title}" url=${url} storage=${storage}>
<${Sidebar} docs=${posts} url=${url} title="Recent Posts" />
<${Main}>
<${BlogContent} title=${title} publishDate=${publishDate}>
<${BlogContent}
title=${title}
publishDate=${publishDate}
author=${author}
authorURL=${authorURL}
>
<${Marked} markdown=${body} components=${components} />
<//BlogContent>
<//Main>
Expand Down

0 comments on commit 7dfea2b

Please sign in to comment.