diff --git a/assets/img/authors/charles.jpg b/assets/img/authors/charles.jpg deleted file mode 100644 index 6b42aaee..00000000 Binary files a/assets/img/authors/charles.jpg and /dev/null differ diff --git a/assets/img/authors/elrick.jpg b/assets/img/authors/elrick.jpg deleted file mode 100644 index 592cc556..00000000 Binary files a/assets/img/authors/elrick.jpg and /dev/null differ diff --git a/assets/img/authors/jacob.jpg b/assets/img/authors/jacob.jpg deleted file mode 100644 index 35b54b73..00000000 Binary files a/assets/img/authors/jacob.jpg and /dev/null differ diff --git a/assets/img/authors/jeffrey.jpg b/assets/img/authors/jeffrey.jpg deleted file mode 100644 index bc44cb64..00000000 Binary files a/assets/img/authors/jeffrey.jpg and /dev/null differ diff --git a/assets/img/authors/jorge.jpg b/assets/img/authors/jorge.jpg deleted file mode 100644 index 622216c4..00000000 Binary files a/assets/img/authors/jorge.jpg and /dev/null differ diff --git a/assets/img/authors/min.jpg b/assets/img/authors/min.jpg deleted file mode 100644 index 6376e9fb..00000000 Binary files a/assets/img/authors/min.jpg and /dev/null differ diff --git a/assets/img/authors/paul.jpg b/assets/img/authors/paul.jpg deleted file mode 100644 index cb57b220..00000000 Binary files a/assets/img/authors/paul.jpg and /dev/null differ diff --git a/assets/img/authors/rachelle.jpg b/assets/img/authors/rachelle.jpg deleted file mode 100644 index f47a836a..00000000 Binary files a/assets/img/authors/rachelle.jpg and /dev/null differ diff --git a/assets/img/authors/taras.jpg b/assets/img/authors/taras.jpg deleted file mode 100644 index 2cf3965b..00000000 Binary files a/assets/img/authors/taras.jpg and /dev/null differ diff --git a/assets/img/authorsAvatars/charles.webp b/assets/img/authorsAvatars/charles.webp new file mode 100644 index 00000000..c1cbf4f9 Binary files /dev/null and b/assets/img/authorsAvatars/charles.webp differ diff --git a/assets/img/authorsAvatars/elrick.webp b/assets/img/authorsAvatars/elrick.webp new file mode 100644 index 00000000..0e570d2b Binary files /dev/null and b/assets/img/authorsAvatars/elrick.webp differ diff --git a/assets/img/authorsAvatars/jacob.webp b/assets/img/authorsAvatars/jacob.webp new file mode 100644 index 00000000..3cda2450 Binary files /dev/null and b/assets/img/authorsAvatars/jacob.webp differ diff --git a/assets/img/authorsAvatars/jeffrey.webp b/assets/img/authorsAvatars/jeffrey.webp new file mode 100644 index 00000000..88808096 Binary files /dev/null and b/assets/img/authorsAvatars/jeffrey.webp differ diff --git a/assets/img/authorsAvatars/jorge.webp b/assets/img/authorsAvatars/jorge.webp new file mode 100644 index 00000000..952363dc Binary files /dev/null and b/assets/img/authorsAvatars/jorge.webp differ diff --git a/assets/img/authorsAvatars/min.webp b/assets/img/authorsAvatars/min.webp new file mode 100644 index 00000000..95ee642e Binary files /dev/null and b/assets/img/authorsAvatars/min.webp differ diff --git a/assets/img/authorsAvatars/paul.webp b/assets/img/authorsAvatars/paul.webp new file mode 100644 index 00000000..3f8a3ccb Binary files /dev/null and b/assets/img/authorsAvatars/paul.webp differ diff --git a/assets/img/authorsAvatars/rachelle.webp b/assets/img/authorsAvatars/rachelle.webp new file mode 100644 index 00000000..321c6c28 Binary files /dev/null and b/assets/img/authorsAvatars/rachelle.webp differ diff --git a/assets/img/authorsAvatars/taras.webp b/assets/img/authorsAvatars/taras.webp new file mode 100644 index 00000000..677c3cd3 Binary files /dev/null and b/assets/img/authorsAvatars/taras.webp differ diff --git a/blog/2023-12-18-announcing-effection-v3/announcing-effection-v3.png b/blog/2023-12-18-announcing-effection-v3/announcing-effection-v3.png deleted file mode 100644 index 59a4f3be..00000000 Binary files a/blog/2023-12-18-announcing-effection-v3/announcing-effection-v3.png and /dev/null differ diff --git a/blog/2023-12-18-announcing-effection-v3/announcing-effection-v3.webp b/blog/2023-12-18-announcing-effection-v3/announcing-effection-v3.webp new file mode 100644 index 00000000..da1ad9ad Binary files /dev/null and b/blog/2023-12-18-announcing-effection-v3/announcing-effection-v3.webp differ diff --git a/blog/2023-12-18-announcing-effection-v3/index.md b/blog/2023-12-18-announcing-effection-v3/index.md index e35fbdeb..229b0bd8 100644 --- a/blog/2023-12-18-announcing-effection-v3/index.md +++ b/blog/2023-12-18-announcing-effection-v3/index.md @@ -5,36 +5,81 @@ author: Charles Lowell description: >- A major milestone in bringing Structured Concurrency and Effects to JavaScript, Effection 3.0 has excellent TypeScript support, a powerful new context API, and harmonizes perfectly with the JavaScript APIs you know and love. tags: [ "javascript", "structured concurrency"] -image: announcing-effection-v3.png +image: announcing-effection-v3.webp --- [Effection] is Structured Concurrency and Effects for JavaScript. -It’s a library purpose-built to help developers write code that can easily manage the most complex concurrent computations whether in the frontend or backend. It provides the guarantees of Structured Concurrency in order to make code leak-proof by default, and it achieves this while being simple to read, understand, and maintain. - -Just over five years ago, in November of 2018, when Structured Concurrency was a little-known peculiarity, we started a helper library to coordinate the many moving parts in some of our open-source projects (such as [Interactors](https://frontside.com/interactors)). Since then, it has grown into a mature project in its own right; deployed in production worldwide, helping developers use Structured Concurrency to push the limit of what is possible with JavaScript. - -Today, we are pushing things further and concentrating even more power into the hands of JavaScript developers with the release of Effection 3.0. This new version refines and simplifies the abstractions we first created in version 2.0 while introducing some new ones of exceptional power. This release has much to look forward to, including the new Context API and an even better TypeScript experience. Most of all, however, we’re proud of how we’ve realigned all of the APIs so that working with Effection feels just like working with JavaScript, only with the protections of structured concurrency baked in. - -That’s why we say more than ever before: *Effection is Structured Concurrency and Effects for JavaScript* +It’s a library purpose-built to help developers write code that can easily +manage the most complex concurrent computations whether in the frontend or +backend. It provides the guarantees of Structured Concurrency in order to make +code leak-proof by default, and it achieves this while being simple to read, +understand, and maintain. + +Just over five years ago, in November of 2018, when Structured Concurrency was a +little-known peculiarity, we started a helper library to coordinate the many +moving parts in some of our open-source projects (such as +[Interactors](https://frontside.com/interactors)). Since then, it has grown into +a mature project in its own right; deployed in production worldwide, helping +developers use Structured Concurrency to push the limit of what is possible with +JavaScript. + +Today, we are pushing things further and concentrating even more power into the +hands of JavaScript developers with the release of Effection 3.0. This new +version refines and simplifies the abstractions we first created in version 2.0 +while introducing some new ones of exceptional power. This release has much to +look forward to, including the new Context API and an even better TypeScript +experience. Most of all, however, we’re proud of how we’ve realigned all of the +APIs so that working with Effection feels just like working with JavaScript, +only with the protections of structured concurrency baked in. + +That’s why we say more than ever before: _Effection is Structured Concurrency +and Effects for JavaScript_ Here’s an overview of some key features you’ll find in Effection 3.0 -\- [Excellent TypeScript Experience](#%EF%B8%8F-excellent-typescript-experience): TypeScript types have been carefully re-imagined to be as small and helpful as possible. +\- +[Excellent TypeScript Experience](#%EF%B8%8F-excellent-typescript-experience): +TypeScript types have been carefully re-imagined to be as small and helpful as +possible. -\- [Embrace of JavaScript](#%EF%B8%8F-embrace-the-javascript): Version 3 is easier to learn and safer to integrate into your projects than ever because of its dedication to following JavaScript standards in spirit and form. +\- [Embrace of JavaScript](#%EF%B8%8F-embrace-the-javascript): Version 3 is +easier to learn and safer to integrate into your projects than ever because of +its dedication to following JavaScript standards in spirit and form. -\- [Context API](#-new-context-api): Shared environmental data (like auth tokens), shared resources, and contextual (aka algebraic) effect handlers have at least one thing in common: They are all made possible by the new Context API. +\- [Context API](#-new-context-api): Shared environmental data (like auth +tokens), shared resources, and contextual (aka algebraic) effect handlers have +at least one thing in common: They are all made possible by the new Context API. -\- [Rebuilt with Delimited Continuations](#-rebuilt-with-delimited-continuations): The v3 API is so simple it feels like it almost isn’t there. But that doesn’t mean there isn’t some serious brainpower behind the ideas that make this possible. +\- +[Rebuilt with Delimited Continuations](#-rebuilt-with-delimited-continuations): +The v3 API is so simple it feels like it almost isn’t there. But that doesn’t +mean there isn’t some serious brainpower behind the ideas that make this +possible. ## 🏷️ Excellent TypeScript Experience -Since version 2.0, Effection itself has been written in TypeScript. As a result, a lot of thought was put into making it pleasant to use there. However, some rough edges remained. - -The first area where the TypeScript experience improved is in the `Operation` type itself. In the 2.x versions of Effection, it was a union of seven member types, one of which was `undefined`. It was unclear what you should set as your return type if you were writing a function that returned an Operation. Compound this with the fact that all Effection APIs were inconsistent in what they returned. Some returned `Resource`, others `OperationFunction`. Now, there is a single `Operation`, which is a [simple interface](https://github.com/thefrontside/effection/blob/45b181f2ca99b512c1b869a10538576bd49d4675/lib/types.ts#L43-L45) that can be used consistently as a function parameter and return type. - -However, the most vexing issue with Effection v2 in TypeScript arose because the generator type prevented the left side of a `yield` expression from being inferred properly. That meant you had to annotate values bound in this way manually. For example, if we had an operation of type `createNumber(): Operation`, then we would have to manually tell TypeScript that the result of `createNumber()` was, in fact, a `number`, even though the return type already clearly indicated this. +Since version 2.0, Effection itself has been written in TypeScript. As a result, +a lot of thought was put into making it pleasant to use there. However, some +rough edges remained. + +The first area where the TypeScript experience improved is in the `Operation` +type itself. In the 2.x versions of Effection, it was a union of seven member +types, one of which was `undefined`. It was unclear what you should set as your +return type if you were writing a function that returned an Operation. Compound +this with the fact that all Effection APIs were inconsistent in what they +returned. Some returned `Resource`, others `OperationFunction`. Now, there +is a single `Operation`, which is a +[simple interface](https://github.com/thefrontside/effection/blob/45b181f2ca99b512c1b869a10538576bd49d4675/lib/types.ts#L43-L45) +that can be used consistently as a function parameter and return type. + +However, the most vexing issue with Effection v2 in TypeScript arose because the +generator type prevented the left side of a `yield` expression from being +inferred properly. That meant you had to annotate values bound in this way +manually. For example, if we had an operation of type +`createNumber(): Operation`, then we would have to manually tell +TypeScript that the result of `createNumber()` was, in fact, a `number`, even +though the return type already clearly indicated this. ```tsx function* operation() { @@ -44,35 +89,56 @@ function* operation() { } ``` -But that was only ever a hint, not a constraint, and a completely bogus annotation would breeze right through the type checker as well. +But that was only ever a hint, not a constraint, and a completely bogus +annotation would breeze right through the type checker as well. ```tsx function* operation() { // we have to declare that `count` is a number let count: number = yield createString(); - return count.toExponential() // TypeError! + return count.toExponential(); // TypeError! } ``` -This was a major Achilles heel for TypeScript developers who wanted to use Effection. They expected the complete experience, not just of compile-time type checking, but also all the language server tooling that having type information enables. Sadly, the TypeScript experience with Effection v2 fell short of what they expected. - -Effection 3.0 makes this completely a non-issue by using [`yield*`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield*) to evaluate operations in the context of another. This has many benefits, including shorter stack traces and lower memory overhead, but one of the greatest is that it completely solves the type inference problem. This is because the type of a `yield*` expression is independent of the type of function that evaluates it. Now, if we try to assign an incompatible type from an operation, we’ll get a type-checking error: +This was a major Achilles heel for TypeScript developers who wanted to use +Effection. They expected the complete experience, not just of compile-time type +checking, but also all the language server tooling that having type information +enables. Sadly, the TypeScript experience with Effection v2 fell short of what +they expected. + +Effection 3.0 makes this completely a non-issue by using +[`yield*`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield*) +to evaluate operations in the context of another. This has many benefits, +including shorter stack traces and lower memory overhead, but one of the +greatest is that it completely solves the type inference problem. This is +because the type of a `yield*` expression is independent of the type of function +that evaluates it. Now, if we try to assign an incompatible type from an +operation, we’ll get a type-checking error: ```tsx function* operation() { //@ts-expect-error `string` is not assignable to `number`! let count = yield* createString(); - return count.toExponential() + return count.toExponential(); } ``` ## ✨ New Context API -Sometimes, you need to share something across a wide range of operations. It could be anything from a simple string like an auth token to a shared service that other code needs to function. But so much depends on this shared thing that you don’t want to pollute your APIs by passing it around as an argument everywhere. Instead, you want it to be “just there” as part of the environment in the same way the `document` reference is “just there” on a web page. +Sometimes, you need to share something across a wide range of operations. It +could be anything from a simple string like an auth token to a shared service +that other code needs to function. But so much depends on this shared thing that +you don’t want to pollute your APIs by passing it around as an argument +everywhere. Instead, you want it to be “just there” as part of the environment +in the same way the `document` reference is “just there” on a web page. -Effection now ships with an API that solves this need elegantly. It is deceptively simple, but don’t let that fool you. The new [`Context`](https://deno.land/x/effection/mod.ts?s=Context) API is jam-packed with power, and it’s one of the features we’re most thrilled to be releasing. +Effection now ships with an API that solves this need elegantly. It is +deceptively simple, but don’t let that fool you. The new +[`Context`](https://deno.land/x/effection/mod.ts?s=Context) API is jam-packed +with power, and it’s one of the features we’re most thrilled to be releasing. -To see this in action, let’s make a `Username` context to track our current user so that we can greet them from an operation. +To see this in action, let’s make a `Username` context to track our current user +so that we can greet them from an operation. ```jsx import { createContext } from "effection"; @@ -81,19 +147,20 @@ import { createContext } from "effection"; const Username = createContext(`username`); ``` -We can now use this context from within any operation! Let’s create an operation to greet the current user: +We can now use this context from within any operation! Let’s create an operation +to greet the current user: ```jsx // greet the current user, whoever that is export function* sayHello() { let username = yield* Username; - console.log(`Hello ${username}!`) + console.log(`Hello ${username}!`); } -await main(function*() { +await main(function* () { yield* Username.set("anonymous"); yield* sayHello(); -}) +}); ``` If we run the above code, it will print the following to the console. @@ -104,25 +171,26 @@ Hello anonymous! But here is the kicker. Context is sensitive to scope. -Because of this, when you set the value of a context, it is only visible to *that scope and its children*. All other scopes are completely unaffected. +Because of this, when you set the value of a context, it is only visible to +_that scope and its children_. All other scopes are completely unaffected. ```jsx -await main(function*() { +await main(function* () { yield* Username.set("anonymous"); yield* sayHello(); - yield* call(function*() { + yield* call(function* () { yield* Username.set("bob"); yield* sayHello(); }); - yield* call(function*() { + yield* call(function* () { yield* Username.set("alice"); yield* sayHello(); }); yield* sayHello(); -}) +}); ``` prints @@ -134,29 +202,46 @@ Hello alice! Hello anonymous! ``` -As you can see, the child operations get their own private definition of `Username` that neither interferes with each other, nor the definition of their parent. +As you can see, the child operations get their own private definition of +`Username` that neither interferes with each other, nor the definition of their +parent. -Call it a foundation for dependency injection, or a basis for algebraic effect handlers, the range of uses for this mechanism are *massive* and we can’t wait to see what folks end up doing with it. +Call it a foundation for dependency injection, or a basis for algebraic effect +handlers, the range of uses for this mechanism are _massive_ and we can’t wait +to see what folks end up doing with it. ## ❤️ Embrace the JavaScript -Our goal with Effection has always been to make it easy to write JavaScript code protected by Structured Concurrency's guardrails. That’s why our project’s tagline is “Structured Concurrency and Effects for JavaScript.” We believe you shouldn’t have to learn an entirely new way to write programs to achieve the benefits of Structured Concurrency. Instead, we believe that the *only* new thing you should have to learn is Structured Concurrency, and everything else should feel like plain JavaScript/TypeScript every step of the way. - -With version 3.0, we’ve doubled down on this idea by re-imagining our APIs as mirrors to existing counterparts in JavaScript. That way, if you know how to do it in JavaScript, you know how to do it in Effection. We call this conversion between vanilla JavaScript and Effection our [“Async Rosetta Stone.”](https://frontside.com/effection/docs/async-rosetta-stone) - -| Async | Effection | -| --- | --- | -| Promise | Operation | -| new Promise() | action() | -| await | yield* | -| async function | function* | -| AsyncIterable | Stream | -| AsyncIterator | Subscription | -| for await | for yield* | - -There is a structured analog for any construct you use for `async` programming. For everything else, *JavaScript and its wider ecosystem is your standard library.* - -Consider [this example from MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function#examples) +Our goal with Effection has always been to make it easy to write JavaScript code +protected by Structured Concurrency's guardrails. That’s why our project’s +tagline is “Structured Concurrency and Effects for JavaScript.” We believe you +shouldn’t have to learn an entirely new way to write programs to achieve the +benefits of Structured Concurrency. Instead, we believe that the _only_ new +thing you should have to learn is Structured Concurrency, and everything else +should feel like plain JavaScript/TypeScript every step of the way. + +With version 3.0, we’ve doubled down on this idea by re-imagining our APIs as +mirrors to existing counterparts in JavaScript. That way, if you know how to do +it in JavaScript, you know how to do it in Effection. We call this conversion +between vanilla JavaScript and Effection our +[“Async Rosetta Stone.”](https://frontside.com/effection/docs/async-rosetta-stone) + +| Async | Effection | +| -------------- | ------------ | +| Promise | Operation | +| new Promise() | action() | +| await | yield* | +| async function | function* | +| AsyncIterable | Stream | +| AsyncIterator | Subscription | +| for await | for yield* | + +There is a structured analog for any construct you use for `async` programming. +For everything else, _JavaScript and its wider ecosystem is your standard +library._ + +Consider +[this example from MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function#examples) ### Async @@ -181,7 +266,7 @@ const add = async function (x) { function resolveAfter2Seconds(x) { return action((resolve) => { let timeout = setTimeout(() => resolve(x), 2000); - return () => clearTimeout(timeout); + return () => clearTimeout(timeout); }); } @@ -193,40 +278,72 @@ const add = function* (x) { }; ``` -Interoperation between these two worlds will always be our project’s foremost concern. For example, creating a `Stream` at runtime is trivial from a reference to an `AsyncIterable` and vice-versa. The same is true for `Subscription` and `AsyncIterator` +Interoperation between these two worlds will always be our project’s foremost +concern. For example, creating a `Stream` at runtime is trivial from a reference +to an `AsyncIterable` and vice-versa. The same is true for `Subscription` and +`AsyncIterator` -Effection is dedicated to keeping this alignment with core JavaScript in place both now and into the future so that developers can have peace of mind knowing that Effection will always be: +Effection is dedicated to keeping this alignment with core JavaScript in place +both now and into the future so that developers can have peace of mind knowing +that Effection will always be: -- Easy to learn for any developer encountering Effection code for the first time. If they know JavaScript, they (mostly) know Effection. -- Easy to adapt to any existing codebase, no matter what other paradigms it may use. +- Easy to learn for any developer encountering Effection code for the first + time. If they know JavaScript, they (mostly) know Effection. +- Easy to adapt to any existing codebase, no matter what other paradigms it may + use. - Easy to “sprinkle” into a codebase without requiring massive refactors. # 🧠 Rebuilt with Delimited Continuations -You might think that a robust implementation of Structured Concurrency and Effects for JavaScript could easily be large, complex, difficult to understand, and a pain to debug. And if you were talking about Effection prior to version 3.0, you might even be right! - -Versions 1.x and 2.x used a combination of state machines and cascading chains of callbacks that fired events and transitioned those state machines to orchestrate code to run at the right time. In a way, those past versions of Effection jumped on the grenade of callback hell so that users didn’t have to. +You might think that a robust implementation of Structured Concurrency and +Effects for JavaScript could easily be large, complex, difficult to understand, +and a pain to debug. And if you were talking about Effection prior to version +3.0, you might even be right! -While that was a worthy tradeoff, Effection 3.0 gets to have its cake and eat it, too by undergirding its entire implementation on the foundation of **delimited continuations**. These curious constructs from the world of functional programming are extraordinarily powerful artifacts. They can express *all* the standard control mechanisms we’re familiar with, from `while` loops to `try/catch`blocks, to algebraic effect handlers. +Versions 1.x and 2.x used a combination of state machines and cascading chains +of callbacks that fired events and transitioned those state machines to +orchestrate code to run at the right time. In a way, those past versions of +Effection jumped on the grenade of callback hell so that users didn’t have to. -Having an abstraction of this power drive Effection v3 means that its core logic can be expressed in only about 150 lines of TypeScript. And because it isn’t based on asynchronous callbacks but instead delimited continuations, execution is linear and straightforwardly follows its source code. As a result, it is an order of magnitude easier to understand and maintain. +While that was a worthy tradeoff, Effection 3.0 gets to have its cake and eat +it, too by undergirding its entire implementation on the foundation of +**delimited continuations**. These curious constructs from the world of +functional programming are extraordinarily powerful artifacts. They can +express _all_ the standard control mechanisms we’re familiar with, +from `while` loops to `try/catch`blocks, to algebraic effect handlers. -We’re packing a more powerful, performant, and maintainable library into a much smaller footprint thanks to delimited continuations. The bundle size has been slashed in half and clocks in at a miniscule 4.6kb gzipped. +Having an abstraction of this power drive Effection v3 means that its core logic +can be expressed in only about 150 lines of TypeScript. And because it isn’t +based on asynchronous callbacks but instead delimited continuations, execution +is linear and straightforwardly follows its source code. As a result, it is an +order of magnitude easier to understand and maintain. -To delve deeper into the subject of delimited continuations in JavaScript, we recommend: +We’re packing a more powerful, performant, and maintainable library into a much +smaller footprint thanks to delimited continuations. The bundle size has been +slashed in half and clocks in at a miniscule 4.6kb gzipped. +To delve deeper into the subject of delimited continuations in JavaScript, we +recommend: -\- [Eric Bower’s talk for Michigan TypeScript](https://www.youtube.com/watch?v=uRbqLGj_6mI) +\- +[Eric Bower’s talk for Michigan TypeScript](https://www.youtube.com/watch?v=uRbqLGj_6mI) \- [On Wikipedia](https://en.wikipedia.org/wiki/Delimited_continuation) -\- [TypeScript tutorial on delimited continuations](https://github.com/cowboyd/delimited-continuations-tutorial) +\- +[TypeScript tutorial on delimited continuations](https://github.com/cowboyd/delimited-continuations-tutorial) ## Get Started With Effection 3.0 Today! -Wondering what the next step is? Whether you're beginning a new project, or wanting to bring more clarity to an existing one, [Effection][effection] is meant to be both the simplest _and_ the easiest way to use structured concurrency and effects in JavaScript. +Wondering what the next step is? Whether you're beginning a new project, or +wanting to bring more clarity to an existing one, [Effection][effection] is +meant to be both the simplest _and_ the easiest way to use structured +concurrency and effects in JavaScript. -Check it out on [GitHub][effection-github], have a look at our [guide to getting started][effection-getting-started], or [drop into our Discord Server][effection-discord] to say hi. We're always eager to talk, and look forward to hearing from you all soon. +Check it out on [GitHub][effection-github], have a look at our +[guide to getting started][effection-getting-started], or +[drop into our Discord Server][effection-discord] to say hi. We're always eager +to talk, and look forward to hearing from you all soon. [effection]: https://frontside.com/effection [effection-github]: https://github.com/thefrontside/effection diff --git a/lib/getAuthorsImage.ts b/lib/getAuthorsImage.ts index dd098fb0..4579e664 100644 --- a/lib/getAuthorsImage.ts +++ b/lib/getAuthorsImage.ts @@ -6,6 +6,6 @@ import { authorsWithImage } from "./authorConfig.ts"; export function getAuthorImage(author: string): string { const firstName = author.split(" ")[0].toLowerCase(); return authorsWithImage.includes(firstName) - ? `/assets/img/authors/${firstName}.jpg` + ? `/assets/img/authorsAvatars/${firstName}.webp` : "/assets/fs-logo-no-text.svg"; } diff --git a/routes/blog-index-route.tsx b/routes/blog-index-route.tsx index 5a2807a1..79e757ef 100644 --- a/routes/blog-index-route.tsx +++ b/routes/blog-index-route.tsx @@ -32,7 +32,7 @@ export function blogIndexRoute() { src={latest.image ? `blog/${latest.id}/${latest.image}` : "/assets/fs-logo-no-text.svg"} - alt="Blog image" + alt="blog image" />
@@ -63,7 +63,7 @@ export function blogIndexRoute() { src={post.image ? `blog/${post.id}/${post.image}` : "/assets/fs-logo-no-text.svg"} - alt="Blog image" + alt="blog image" />

{post.title}

diff --git a/routes/blog-post-route.tsx b/routes/blog-post-route.tsx index 6a25aa90..4130cb7f 100644 --- a/routes/blog-post-route.tsx +++ b/routes/blog-post-route.tsx @@ -53,7 +53,13 @@ export function blogPostRoute(): JSXHandler { ))}
- + blog image