Skip to content

guybedford/proposal-import-buffer

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Proposal Import Buffer

Champions: @styfle

Status: Stage 0.

Please leave any feedback in the issues, thanks!

Synopsis

This proposal is buit on top of import attributes and immutable arraybuffer to add the ability to import arbitrary bytes in a common way across JavaScript environments.

Developers will then be able to import the buffer as follows:

import buffer from "./photo.png" with { type: "buffer" };
import("photo.png", { with: { type: "buffer" } });

Note: a similar proposal was mentioned in whatwg/html#9444

Motivation

In a similar manner to why JSON modules are useful, importing raw bytes is useful to extend this behavior to all files. This proposal provides an isomorphic way to read a file, regardless of the JavaScript environment.

For example, a developer may want to read a .png file to process an image or .woff to process a font and pass the buffer into isomorphic tools like satori.

Today, the developer must detect the platform in order to read the buffer.

async function getBuffer(path) {
  if (typeof Deno !== "undefined") {
    const result = await Deno.readFile(path);
    return result.buffer;
  }
  if (typeof Bun !== "undefined") {
    const buffer = await Bun.file(path).arrayBuffer();
    return buffer;
  }
  if (typeof require !== "undefined") {
    const fs = require("fs/promises");
    const result = await fs.readFile(path);
    return result.buffer;
  }
  if (typeof window !== "undefined") {
    const response = await fetch(path);
    const buffer = await response.arrayBuffer();
    return buffer;
  }
  throw new Error("Unsupported runtime");
}

const buffer = await getBuffer("./photo.png");

We can maximize portability and reduce boilerplate by turning this into a single line of code:

import buffer from "./photo.png" with { type: "buffer" };

Using an import also provides opportunity for further optimizations when using a bundler. For example, bundlers can statically analyze this import and inline as base64.

const buffer = Uint8Array.fromBase64("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkqAcAAIUAgUW0RjgAAAAASUVORK5CYII=").buffer.transferToImmutable()

Proposed semantics and interoperability

If a module import has an attribute with key type and value buffer, the host is required to either fail the import, or treat it as an immutable ArrayBuffer. The ArrayBuffer object is the default export of the module (which has no named exports).

In browser environments, this will be equivalent to fetch() such that sec-fetch-dest will be empty. The response content-type will be ignored.

In "local" desktop/server/embedded, this will be equivalent to a file read. The file extension will be ignored.

All of the import statements in the module graph that address the same module will evaluate to the same immutable ArrayBuffer object.

FAQ

How would this proposal work with caching?

The determination of whether the type attribute is part of the module cache key is left up to hosts (as it is for all import attributes).

For example, import "foo" and import "foo" with { type: "buffer" } may return the same module in one host, and different modules with another host. Both are valid implementations.

However, a dynamic import and a static import with the same type will return the same module, regardless of host.

See discussion in Issue tc39#4

Is there any prior art?

Deno 2.4 added support in July 2025 to inline a Uint8Array

import imageBytes from "./image.png" with { type: "bytes" };

Bun 1.1.7 added a similar feature in May 2024 to inline a string of text

import html from "./index.html" with { type: "text" };

webpack added asset modules to inline a base64 data URI via url-loader in 4.x and now asset/inline in 5.x

import logo from "./images/logo.svg"

esbuild added a binary loader to inline a Uint8Array

import uint8array from "./example.data"

Parcel added a data url scheme to inline a base64 data URI

import background from "data-url:./background.png";

Moddable added a Resource class to inline a host buffer for embedded systems

let resource = new Resource("logo.bmp");

Why not mutable?

Mutable can be problematic for several reasons:

  • may need multiple copies of the buffer in memory to avoid different underlying bytes for import(specifier, { type: "json" }) and import(specifier, { type: "buffer" })
  • may cause unexpected behavior when multiple modules import the same buffer and detach (say postMessage() or transferToImmutable()) in one module which would cause it to become detached in the other module too
  • may cause excessive RAM usage for embedded system (immutable could use ROM instead)
  • may cause excessive memory when tracking source maps
  • may cause an undeniable global communication channel

See discussion in Issue tc39#2 and tc39#5

Why not Uint8Array?

Uint8Array is one array-like view of an underlying ArrayBuffer, but there are many views (see TypedArray) because there are many different ways that one might want to access the content of the buffer.

See discussion in Issue tc39#5

Why not Blob?

Blob is part of the W3C File API, not part of JavaScript, so it is not a viable solution to include in a TC39 Proposal. Furthermore, Blob typically includes a MIME Type but this proposal ignores the type.

Why not ReableStream?

ReadableStream is part of the WHATWG Streams, bot part of JavaScript, so it is not a viable solution to include in a TC39 Proposal. Furthermore, there is no helper method to turn a stream into a buffer so this won't solve the original motivation of writing isomorphic JavaScript.

See discussion in Issue tc39#3

About

A modest proposal for importing bytes in javascript

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

No packages published

Languages

  • JavaScript 74.7%
  • HTML 25.3%