text-line-transform-stream.ts 2.0 KB

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