nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
196 lines (175 loc) • 4.94 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/webstreams/encoding.js
import { TextDecoder, TextEncoder } from "nstdlib/lib/internal/encoding";
import { TransformStream } from "nstdlib/lib/internal/webstreams/transformstream";
import { customInspect } from "nstdlib/lib/internal/webstreams/util";
import { codes as __codes__ } from "nstdlib/lib/internal/errors";
import {
customInspectSymbol as kInspect,
kEmptyObject,
kEnumerableProperty,
} from "nstdlib/lib/internal/util";
const { ERR_INVALID_THIS } = __codes__;
/**
* @typedef {import('./readablestream').ReadableStream} ReadableStream
* @typedef {import('./writablestream').WritableStream} WritableStream
*/
class TextEncoderStream {
#pendingHighSurrogate = null;
#handle;
#transform;
constructor() {
this.#handle = new TextEncoder();
this.#transform = new TransformStream({
transform: (chunk, controller) => {
// https://encoding.spec.whatwg.org/#encode-and-enqueue-a-chunk
chunk = String(chunk);
let finalChunk = "";
for (let i = 0; i < chunk.length; i++) {
const item = chunk[i];
const codeUnit = String.prototype.charCodeAt.call(item, 0);
if (this.#pendingHighSurrogate !== null) {
const highSurrogate = this.#pendingHighSurrogate;
this.#pendingHighSurrogate = null;
if (0xdc00 <= codeUnit && codeUnit <= 0xdfff) {
finalChunk += highSurrogate + item;
continue;
}
finalChunk += "\uFFFD";
}
if (0xd800 <= codeUnit && codeUnit <= 0xdbff) {
this.#pendingHighSurrogate = item;
continue;
}
if (0xdc00 <= codeUnit && codeUnit <= 0xdfff) {
finalChunk += "\uFFFD";
continue;
}
finalChunk += item;
}
if (finalChunk) {
const value = this.#handle.encode(finalChunk);
controller.enqueue(value);
}
},
flush: (controller) => {
// https://encoding.spec.whatwg.org/#encode-and-flush
if (this.#pendingHighSurrogate !== null) {
controller.enqueue(new Uint8Array([0xef, 0xbf, 0xbd]));
}
},
});
}
/**
* @readonly
* @type {string}
*/
get encoding() {
return this.#handle.encoding;
}
/**
* @readonly
* @type {ReadableStream}
*/
get readable() {
return this.#transform.readable;
}
/**
* @readonly
* @type {WritableStream}
*/
get writable() {
return this.#transform.writable;
}
[kInspect](depth, options) {
if (this == null) throw new ERR_INVALID_THIS("TextEncoderStream");
return customInspect(depth, options, "TextEncoderStream", {
encoding: this.#handle.encoding,
readable: this.#transform.readable,
writable: this.#transform.writable,
});
}
}
class TextDecoderStream {
#handle;
#transform;
/**
* @param {string} [encoding]
* @param {{
* fatal? : boolean,
* ignoreBOM? : boolean,
* }} [options]
*/
constructor(encoding = "utf-8", options = kEmptyObject) {
this.#handle = new TextDecoder(encoding, options);
this.#transform = new TransformStream({
transform: (chunk, controller) => {
const value = this.#handle.decode(chunk, { stream: true });
if (value) controller.enqueue(value);
},
flush: (controller) => {
const value = this.#handle.decode();
if (value) controller.enqueue(value);
controller.terminate();
},
});
}
/**
* @readonly
* @type {string}
*/
get encoding() {
return this.#handle.encoding;
}
/**
* @readonly
* @type {boolean}
*/
get fatal() {
return this.#handle.fatal;
}
/**
* @readonly
* @type {boolean}
*/
get ignoreBOM() {
return this.#handle.ignoreBOM;
}
/**
* @readonly
* @type {ReadableStream}
*/
get readable() {
return this.#transform.readable;
}
/**
* @readonly
* @type {WritableStream}
*/
get writable() {
return this.#transform.writable;
}
[kInspect](depth, options) {
if (this == null) throw new ERR_INVALID_THIS("TextDecoderStream");
return customInspect(depth, options, "TextDecoderStream", {
encoding: this.#handle.encoding,
fatal: this.#handle.fatal,
ignoreBOM: this.#handle.ignoreBOM,
readable: this.#transform.readable,
writable: this.#transform.writable,
});
}
}
Object.defineProperties(TextEncoderStream.prototype, {
encoding: kEnumerableProperty,
readable: kEnumerableProperty,
writable: kEnumerableProperty,
});
Object.defineProperties(TextDecoderStream.prototype, {
encoding: kEnumerableProperty,
fatal: kEnumerableProperty,
ignoreBOM: kEnumerableProperty,
readable: kEnumerableProperty,
writable: kEnumerableProperty,
});
export { TextEncoderStream };
export { TextDecoderStream };