Skip to content

Commit

Permalink
[data-uri-to-buffer] Refactor to return an ArrayBuffer instead of a…
Browse files Browse the repository at this point in the history
… Node.js `Buffer` (#252)

Breaking change refactor to return an `ArrayBuffer` instead of a Node.js `Buffer`.

This change is being made to make the package platform-agnostic, and work in web browsers or other non-Node.js environments without polyfills.

For Node.js users of this package, you can get a Node.js `Buffer` instance from an `ArrayBuffer` like so:

```typescript
const uri = 'data:,Hello%2C%20World!';
const parsed = dataUriToBuffer(uri);
const buffer = Buffer.from(parsed.buffer);
// `buffer` is a Node.js Buffer
```
  • Loading branch information
TooTallNate authored Sep 30, 2023
1 parent aaebfa4 commit 52b458f
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 98 deletions.
16 changes: 16 additions & 0 deletions .changeset/lucky-items-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'data-uri-to-buffer': major
---

Refactor to return an `ArrayBuffer` instead of a Node.js `Buffer`.

This change is being made to make the package platform-agnostic, and work in web browsers or other non-Node.js environments without polyfills.

For Node.js users of this package, you can get a Node.js `Buffer` instance from an `ArrayBuffer` like so:

```typescript
const uri = 'data:,Hello%2C%20World!';
const parsed = dataUriToBuffer(uri);
const buffer = Buffer.from(parsed.buffer);
// `buffer` is a Node.js Buffer
```
38 changes: 25 additions & 13 deletions packages/data-uri-to-buffer/README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,60 @@
data-uri-to-buffer
==================
### Generate a Buffer instance from a [Data URI][rfc] string
### Create an ArrayBuffer instance from a [Data URI][rfc] string

This module accepts a ["data" URI][rfc] String of data, and returns a
node.js `Buffer` instance with the decoded data.
This module accepts a ["data" URI][rfc] String of data, and returns
an `ArrayBuffer` instance with the decoded data.

This module is intended to work on a large variety of JavaScript
runtimes, including Node.js and web browsers.

Example
-------

``` js
```typescript
import { dataUriToBuffer } from 'data-uri-to-buffer';

// plain-text data is supported
let uri = 'data:,Hello%2C%20World!';
let decoded = dataUriToBuffer(uri);
console.log(decoded.toString());
let parsed = dataUriToBuffer(uri);
console.log(new TextDecoder().decode(parsed.buffer));
// 'Hello, World!'

// base64-encoded data is supported
uri = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D';
decoded = dataUriToBuffer(uri);
console.log(decoded.toString());
parsed = dataUriToBuffer(uri);
console.log(new TextDecoder().decode(parsed.buffer));
// 'Hello, World!'
```


API
---

### dataUriToBuffer(String uri) → Buffer
```typescript
export interface ParsedDataURI {
type: string;
typeFull: string;
charset: string;
buffer: ArrayBuffer;
}
```

### dataUriToBuffer(uri: string | URL) → ParsedDataURI

The `type` property on the Buffer instance gets set to the main type portion of
The `type` property gets set to the main type portion of
the "mediatype" portion of the "data" URI, or defaults to `"text/plain"` if not
specified.

The `typeFull` property on the Buffer instance gets set to the entire
The `typeFull` property gets set to the entire
"mediatype" portion of the "data" URI (including all parameters), or defaults
to `"text/plain;charset=US-ASCII"` if not specified.

The `charset` property on the Buffer instance gets set to the Charset portion of
The `charset` property gets set to the Charset portion of
the "mediatype" portion of the "data" URI, or defaults to `"US-ASCII"` if the
entire type is not specified, or defaults to `""` otherwise.

*Note*: If the only the main type is specified but not the charset, e.g.
*Note*: If only the main type is specified but not the charset, e.g.
`"data:text/plain,abc"`, the charset is set to the empty string. The spec only
defaults to US-ASCII as charset if the entire type is not specified.

Expand Down
2 changes: 1 addition & 1 deletion packages/data-uri-to-buffer/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "data-uri-to-buffer",
"version": "5.0.1",
"description": "Generate a Buffer instance from a Data URI string",
"description": "Create an ArrayBuffer instance from a Data URI string",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
Expand Down
76 changes: 60 additions & 16 deletions packages/data-uri-to-buffer/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,66 @@
export interface MimeBuffer extends Buffer {
export interface ParsedDataURI {
type: string;
typeFull: string;
charset: string;
buffer: ArrayBuffer;
}

function base64ToArrayBuffer(base64: string) {
const chars =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

const bytes = [];

for (let i = 0; i < base64.length; i += 4) {
const idx0 = chars.indexOf(base64.charAt(i));
const idx1 = chars.indexOf(base64.charAt(i + 1));
const idx2 =
base64.charAt(i + 2) === '='
? 0
: chars.indexOf(base64.charAt(i + 2));
const idx3 =
base64.charAt(i + 3) === '='
? 0
: chars.indexOf(base64.charAt(i + 3));

const bin0 = (idx0 << 2) | (idx1 >> 4);
const bin1 = ((idx1 & 15) << 4) | (idx2 >> 2);
const bin2 = ((idx2 & 3) << 6) | idx3;

bytes.push(bin0);
if (base64.charAt(i + 2) !== '=') bytes.push(bin1);
if (base64.charAt(i + 3) !== '=') bytes.push(bin2);
}

const buffer = new ArrayBuffer(bytes.length);
const view = new Uint8Array(buffer);
view.set(bytes);
return buffer;
}

function stringToBuffer(str: string): ArrayBuffer {
// Create a buffer with length equal to the string length
const buffer = new ArrayBuffer(str.length);

// Create a view to manipulate the buffer content
const view = new Uint8Array(buffer);

// Iterate over the string and populate the buffer with ASCII codes
for (let i = 0; i < str.length; i++) {
view[i] = str.charCodeAt(i);
}

return buffer;
}

/**
* Returns a `Buffer` instance from the given data URI `uri`.
*
* @param {String} uri Data URI to turn into a Buffer instance
* @returns {Buffer} Buffer instance from Data URI
* @api public
*/
export function dataUriToBuffer(uri: string): MimeBuffer {
export function dataUriToBuffer(uri: string | URL): ParsedDataURI {
uri = String(uri);

if (!/^data:/i.test(uri)) {
throw new TypeError(
'`uri` does not appear to be a Data URI (must begin with "data:")'
Expand Down Expand Up @@ -51,18 +100,13 @@ export function dataUriToBuffer(uri: string): MimeBuffer {
}

// get the encoded data portion and decode URI-encoded chars
const encoding = base64 ? 'base64' : 'ascii';
const data = unescape(uri.substring(firstComma + 1));
const buffer = Buffer.from(data, encoding) as MimeBuffer;

// set `.type` and `.typeFull` properties to MIME type
buffer.type = type;
buffer.typeFull = typeFull;

// set the `.charset` property
buffer.charset = charset;
const buffer = base64 ? base64ToArrayBuffer(data) : stringToBuffer(data);

return buffer;
return {
type,
typeFull,
charset,
buffer,
};
}

export default dataUriToBuffer;
Loading

1 comment on commit 52b458f

@vercel
Copy link

@vercel vercel bot commented on 52b458f Sep 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.