text-line-transform-stream.ts 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
  2. // This module is browser compatible.
  3. // Modified by Sukka (https://skk.moe) to increase compatibility and performance with Bun.
  4. import { TransformStream } from 'node:stream/web';
  5. interface TextLineStreamOptions {
  6. /** Allow splitting by solo \r */
  7. allowCR?: boolean
  8. }
  9. /** Transform a stream into a stream where each chunk is divided by a newline,
  10. * be it `\n` or `\r\n`. `\r` can be enabled via the `allowCR` option.
  11. *
  12. * ```ts
  13. * const res = await fetch('https://example.com');
  14. * const lines = res.body!
  15. * .pipeThrough(new TextDecoderStream())
  16. * .pipeThrough(new TextLineStream());
  17. * ```
  18. */
  19. export class TextLineStream extends TransformStream<string, string> {
  20. // private __buf = '';
  21. constructor({
  22. allowCR = false
  23. }: TextLineStreamOptions = {}) {
  24. let __buf = '';
  25. super({
  26. transform(chunk, controller) {
  27. chunk = __buf + chunk;
  28. for (; ;) {
  29. const lfIndex = chunk.indexOf('\n');
  30. if (allowCR) {
  31. const crIndex = chunk.indexOf('\r');
  32. if (
  33. crIndex !== -1 && crIndex !== (chunk.length - 1)
  34. && (lfIndex === -1 || (lfIndex - 1) > crIndex)
  35. ) {
  36. controller.enqueue(chunk.slice(0, crIndex));
  37. chunk = chunk.slice(crIndex + 1);
  38. continue;
  39. }
  40. }
  41. if (lfIndex !== -1) {
  42. let crOrLfIndex = lfIndex;
  43. if (chunk[lfIndex - 1] === '\r') {
  44. crOrLfIndex--;
  45. }
  46. controller.enqueue(chunk.slice(0, crOrLfIndex));
  47. chunk = chunk.slice(lfIndex + 1);
  48. continue;
  49. }
  50. break;
  51. }
  52. __buf = chunk;
  53. },
  54. flush(controller) {
  55. if (__buf.length > 0) {
  56. // eslint-disable-next-line sukka-ts/string/prefer-string-starts-ends-with -- performance
  57. if (allowCR && __buf[__buf.length - 1] === '\r') {
  58. controller.enqueue(__buf.slice(0, -1));
  59. } else {
  60. controller.enqueue(__buf);
  61. }
  62. }
  63. }
  64. });
  65. }
  66. }