Closed dawaltconley closed 7 months ago
Additionally, the internal functions would be better exposed as a series of method chains rather than a bunch of disconnected pure functions.
I'd like to be able to write something like:
const responsive = await image(src, opts)
.fromSizes(sizes) // returns an object with the metadata and various methods for processing it
.toHtml({ alt }) // could also call .toCss() or something
Handling async methods might be tricky. Ideally I'd still want to be able await .fromSizes
and then pass its value to a synchronous toHtml
method call on the client.
New API in v0.5.0 should address this.
Currently, nearly all public methods follow a convention where the first argument takes a path to an image (
string | URL | Buffer
) and the second argument takes an object of various options. The motivation behind this was to have methods that could be easily called from markup, with static site templating languages in mind:One downside of this approach is that it mixes different types of arguments into the same object: both items like
alt
that will end up in the markup, and processing commands likeformats
. Then you havesizes
, which serves both purposes. This is a little hard to keep track of internally.A further complication is that, unlike templating languages, components are not limited to the server. A jsx component likely needs to receive some information about the generated images from a prior build step. So its API would look more like:
This is pretty close to the way
eleventy-img
works, where one function (Image
) takes a set of options that resize the image and return a metadata object, and another function (generateHtml
) takes the metadata object as its first argument its second argument is an object that defines html attributes. This nicely separates concerns, and it's be possible to do this internally with wrapper methods that flatten these parameters for use in templating shortcodes.The problem with this separation of concerns is: where does
sizes
go? The whole point of this library is thatsizes
can define the images that must be generated with both better precision and abstraction than a custom data type. That means I want server-side methods to use it. But it also needs to go to the client, and it's important that it's the same sizes method going to the client.The most straightforward approach is to pass sizes through
metadataFromSizes
as part of the returned metadata object. But that raises the question: what else should be passed through? In the above example, thealt
attribute works fine, but if we're passing through an array of metadata for a set of images, we'll probably want any html property that depends on the image src (such as alt) to be part of that metadata.I could go to the other extreme and just pass through fully rendered strings or React server components (where supported). This makes it more difficult, however, to style elements based on their context. It seems useful to at least be able to set
classNames
on the client since styles can depend on the surrounding elements. The easiest way to allow this is for both the server-side and client code to accept html attributes, so any attribute can be paired with a specificsrc
and can also be overridden.All that said, it probably is worth separating these concerns internally and only mixing them in the api of public methods.