All files excerpt.ts

100% Statements 14/14
100% Branches 15/15
100% Functions 1/1
100% Lines 14/14

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146                1310x     1310x   1310x 1x 1x     1309x 1309x   1310x 902x     407x     1310x 1310x 306x     407x                                                                                                                                                                                                                                
import { defaults } from "./defaults.ts";
import type { GrayMatterFile, GrayMatterOptions } from "./types.ts";
import { getStringProp, isString } from "./utils.ts";
 
/**
 * Extract excerpt from file content
 */
export function excerpt(file: GrayMatterFile, options?: GrayMatterOptions): GrayMatterFile {
  const opts = defaults(options);
 
  // Ensure data is an object (defensive check for external callers)
  file.data ??= {};
 
  if (typeof opts.excerpt === "function") {
    opts.excerpt(file, opts);
    return file;
  }
 
  const dataSep = getStringProp(file.data, "excerpt_separator");
  const sep = dataSep !== "" ? dataSep : opts.excerpt_separator;
 
  if (sep == null && (opts.excerpt === false || opts.excerpt == null)) {
    return file;
  }
 
  const delimiter = isString(opts.excerpt) ? opts.excerpt : (sep ?? opts.delimiters[0]);
 
  // if enabled, get the excerpt defined after front-matter
  const idx = file.content.indexOf(delimiter);
  if (idx !== -1) {
    file.excerpt = file.content.slice(0, idx);
  }
 
  return file;
}
 
if (import.meta.vitest) {
  const { fc, test } = await import("@fast-check/vitest");
 
  const makeFile = (content: string, data: Record<string, unknown> = {}): GrayMatterFile => ({
    content,
    data,
    excerpt: "",
    orig: new TextEncoder().encode(content),
    language: "yaml",
    matter: "",
    isEmpty: false,
    stringify: () => "",
  });
 
  describe("excerpt", () => {
    it("should extract excerpt using default delimiter", () => {
      const file = makeFile("This is excerpt\n---\nThis is content");
      const result = excerpt(file, { excerpt: true });
      expect(result.excerpt).toBe("This is excerpt\n");
    });
 
    it("should extract excerpt using custom string delimiter", () => {
      const file = makeFile("This is excerpt\n<!-- more -->\nThis is content");
      const result = excerpt(file, { excerpt: "<!-- more -->" });
      expect(result.excerpt).toBe("This is excerpt\n");
    });
 
    it("should use excerpt_separator from options", () => {
      const file = makeFile("Excerpt here\n***\nContent here");
      const result = excerpt(file, { excerpt: true, excerpt_separator: "***" });
      expect(result.excerpt).toBe("Excerpt here\n");
    });
 
    it("should use excerpt_separator from file.data", () => {
      const file = makeFile("Excerpt\n<!-- end -->\nContent", {
        excerpt_separator: "<!-- end -->",
      });
      const result = excerpt(file, { excerpt: true });
      expect(result.excerpt).toBe("Excerpt\n");
    });
 
    it("should not extract when excerpt is false", () => {
      const file = makeFile("Some content\n---\nMore content");
      const result = excerpt(file, { excerpt: false });
      expect(result.excerpt).toBe("");
    });
 
    it("should not modify when delimiter not found", () => {
      const file = makeFile("No delimiter here");
      const result = excerpt(file, { excerpt: true });
      expect(result.excerpt).toBe("");
    });
 
    it("should call custom excerpt function", () => {
      const file = makeFile("Custom content");
      const customFn = vi.fn((f: GrayMatterFile) => {
        f.excerpt = "Custom excerpt";
      });
      const result = excerpt(file, { excerpt: customFn });
      expect(customFn).toHaveBeenCalledOnce();
      expect(result.excerpt).toBe("Custom excerpt");
    });
 
    it("should initialize file.data if null", () => {
      const file = makeFile("content");
      file.data = null as unknown as Record<string, unknown>;
      const result = excerpt(file, {});
      expect(result.data).toEqual({});
    });
 
    test.prop([
      fc.string({ minLength: 1, maxLength: 50 }),
      fc.string({ minLength: 1, maxLength: 50 }),
    ])("should extract correct excerpt when delimiter exists", (excerptText, contentText) => {
      const delimiter = "---";
      const content = `${excerptText}\n${delimiter}\n${contentText}`;
      const file = makeFile(content);
      const result = excerpt(file, { excerpt: true });
 
      expect(result.excerpt).toBe(`${excerptText}\n`);
    });
 
    test.prop([fc.string({ minLength: 1, maxLength: 100 })])(
      "should not extract when no delimiter in content",
      (content) => {
        const safeContent = content.replace(/---/g, "___");
        const file = makeFile(safeContent);
        const result = excerpt(file, { excerpt: true });
        expect(result.excerpt).toBe("");
      },
    );
 
    test.prop([
      fc.string({ minLength: 1, maxLength: 20 }),
      fc.string({ minLength: 1, maxLength: 20 }),
      fc
        .string({ minLength: 1, maxLength: 10 })
        .filter((s) => !s.includes("\n") && !/[[\]{}()*+?.,\\^$|#]/.test(s)),
    ])("should work with any custom delimiter", (excerptText, contentText, customDelimiter) => {
      const safeExcerpt = excerptText.replaceAll(customDelimiter, "");
      if (safeExcerpt === "") return;
      const content = `${safeExcerpt}\n${customDelimiter}\n${contentText}`;
      const file = makeFile(content);
      const result = excerpt(file, { excerpt: customDelimiter });
 
      expect(result.excerpt).toBe(`${safeExcerpt}\n`);
    });
  });
}