gray-matter-es 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/defaults.mjs +19 -0
  4. package/dist/defaults.mjs.map +1 -0
  5. package/dist/engines.d.mts +8 -0
  6. package/dist/engines.mjs +63 -0
  7. package/dist/engines.mjs.map +1 -0
  8. package/dist/excerpt.mjs +26 -0
  9. package/dist/excerpt.mjs.map +1 -0
  10. package/dist/index.d.mts +12 -0
  11. package/dist/index.mjs +126 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_chars.mjs +45 -0
  14. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_chars.mjs.map +1 -0
  15. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_dumper_state.mjs +437 -0
  16. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_dumper_state.mjs.map +1 -0
  17. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_loader_state.mjs +909 -0
  18. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_loader_state.mjs.map +1 -0
  19. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_schema.mjs +115 -0
  20. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_schema.mjs.map +1 -0
  21. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/binary.mjs +89 -0
  22. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/binary.mjs.map +1 -0
  23. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/bool.mjs +35 -0
  24. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/bool.mjs.map +1 -0
  25. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/float.mjs +55 -0
  26. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/float.mjs.map +1 -0
  27. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/int.mjs +114 -0
  28. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/int.mjs.map +1 -0
  29. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/map.mjs +15 -0
  30. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/map.mjs.map +1 -0
  31. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/merge.mjs +11 -0
  32. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/merge.mjs.map +1 -0
  33. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/nil.mjs +20 -0
  34. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/nil.mjs.map +1 -0
  35. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/omap.mjs +28 -0
  36. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/omap.mjs.map +1 -0
  37. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/pairs.mjs +19 -0
  38. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/pairs.mjs.map +1 -0
  39. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/regexp.mjs +26 -0
  40. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/regexp.mjs.map +1 -0
  41. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/seq.mjs +11 -0
  42. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/seq.mjs.map +1 -0
  43. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/set.mjs +14 -0
  44. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/set.mjs.map +1 -0
  45. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/str.mjs +11 -0
  46. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/str.mjs.map +1 -0
  47. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/timestamp.mjs +54 -0
  48. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/timestamp.mjs.map +1 -0
  49. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/undefined.mjs +19 -0
  50. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_type/undefined.mjs.map +1 -0
  51. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_utils.mjs +14 -0
  52. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/_utils.mjs.map +1 -0
  53. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/mod.mjs +4 -0
  54. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/parse.mjs +50 -0
  55. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/parse.mjs.map +1 -0
  56. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/stringify.mjs +32 -0
  57. package/dist/node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/stringify.mjs.map +1 -0
  58. package/dist/parse.mjs +13 -0
  59. package/dist/parse.mjs.map +1 -0
  60. package/dist/stringify.mjs +52 -0
  61. package/dist/stringify.mjs.map +1 -0
  62. package/dist/to-file.mjs +44 -0
  63. package/dist/to-file.mjs.map +1 -0
  64. package/dist/types.d.mts +85 -0
  65. package/dist/utils.mjs +60 -0
  66. package/dist/utils.mjs.map +1 -0
  67. package/package.json +61 -0
  68. package/src/defaults.ts +17 -0
  69. package/src/engines.ts +217 -0
  70. package/src/excerpt.ts +146 -0
  71. package/src/index.ts +481 -0
  72. package/src/parse.ts +9 -0
  73. package/src/stringify.ts +187 -0
  74. package/src/to-file.ts +178 -0
  75. package/src/types.ts +84 -0
  76. package/src/utils.ts +158 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ryoppippi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # gray-matter-es
2
+
3
+ ESM-only [gray-matter](https://github.com/jonschlinkert/gray-matter) implementation.
4
+
5
+ ## Features
6
+
7
+ - 🚀 ESM-only, no CommonJS
8
+ - 📦 Zero runtime dependencies (YAML parser bundled from [@std/yaml](https://jsr.io/@std/yaml))
9
+ - 🔷 Full TypeScript support with strict types
10
+ - ✅ API compatible with gray-matter
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install gray-matter-es
16
+ # or
17
+ pnpm add gray-matter-es
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```typescript
23
+ import matter from "gray-matter-es";
24
+
25
+ // Parse front matter
26
+ const file = matter("---\ntitle: Hello\n---\nThis is content");
27
+ console.log(file.data); // { title: 'Hello' }
28
+ console.log(file.content); // 'This is content'
29
+
30
+ // Stringify
31
+ const str = matter.stringify("content", { title: "Hello" });
32
+ // ---
33
+ // title: Hello
34
+ // ---
35
+ // content
36
+
37
+ // Read from file (Node.js)
38
+ const fileFromDisk = matter.read("./post.md");
39
+
40
+ // Test if string has front matter
41
+ matter.test("---\ntitle: Hello\n---"); // true
42
+
43
+ // Detect language
44
+ matter.language("---json\n{}\n---"); // { raw: 'json', name: 'json' }
45
+ ```
46
+
47
+ ### Custom Delimiters
48
+
49
+ ```typescript
50
+ const file = matter("~~~\ntitle: Hello\n~~~\ncontent", {
51
+ delimiters: "~~~",
52
+ });
53
+ ```
54
+
55
+ ### JSON Front Matter
56
+
57
+ ```typescript
58
+ const file = matter('---json\n{"title": "Hello"}\n---\ncontent');
59
+ console.log(file.data); // { title: 'Hello' }
60
+ console.log(file.language); // 'json'
61
+ ```
62
+
63
+ ### Excerpt
64
+
65
+ ```typescript
66
+ const file = matter("---\ntitle: Hello\n---\nexcerpt\n---\ncontent", {
67
+ excerpt: true,
68
+ });
69
+ console.log(file.excerpt); // 'excerpt\n'
70
+ ```
71
+
72
+ ## API
73
+
74
+ ### `matter(input, options?)`
75
+
76
+ Parse front matter from a string or buffer.
77
+
78
+ **Parameters:**
79
+
80
+ - `input` - String, Buffer, or object with `content` property
81
+ - `options` - Optional configuration
82
+
83
+ **Returns:** `GrayMatterFile` object with:
84
+
85
+ - `data` - Parsed front matter data
86
+ - `content` - Content after front matter
87
+ - `excerpt` - Extracted excerpt (if enabled)
88
+ - `orig` - Original input as Buffer
89
+ - `language` - Detected/specified language
90
+ - `matter` - Raw front matter string
91
+ - `isEmpty` - True if front matter block was empty
92
+ - `stringify(data?, options?)` - Stringify the file back
93
+
94
+ ### `matter.stringify(file, data?, options?)`
95
+
96
+ Stringify data to front matter and append content.
97
+
98
+ ### `matter.read(filepath, options?)`
99
+
100
+ Synchronously read and parse a file.
101
+
102
+ ### `matter.test(str, options?)`
103
+
104
+ Test if a string has front matter.
105
+
106
+ ### `matter.language(str, options?)`
107
+
108
+ Detect the language specified after the opening delimiter.
109
+
110
+ ### Options
111
+
112
+ - `language` - Language to use for parsing (default: `'yaml'`)
113
+ - `delimiters` - Custom delimiters (default: `'---'`)
114
+ - `excerpt` - Enable excerpt extraction (`true`, string delimiter, or function)
115
+ - `excerpt_separator` - Custom excerpt separator
116
+ - `engines` - Custom parsing/stringifying engines
117
+
118
+ ## Differences from gray-matter
119
+
120
+ - ESM-only (no CommonJS support)
121
+ - Uses [`@std/yaml`](https://jsr.io/@std/yaml) instead of `js-yaml`
122
+ - Removed JavaScript front matter engine (security: avoids `eval`)
123
+ - Removed deprecated options (`lang`, `delims`, `parsers`)
124
+ - Removed `section-matter` support
125
+ - TypeScript-first with strict types
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,19 @@
1
+ import { arrayify } from "./utils.mjs";
2
+
3
+ //#region src/defaults.ts
4
+ /**
5
+ * Apply default options
6
+ */
7
+ function defaults(options) {
8
+ const delims = arrayify(options?.delimiters ?? "---");
9
+ const delimiters = delims.length === 1 ? [delims[0], delims[0]] : [delims[0], delims[1]];
10
+ return {
11
+ ...options,
12
+ delimiters,
13
+ language: options?.language ?? "yaml"
14
+ };
15
+ }
16
+
17
+ //#endregion
18
+ export { defaults };
19
+ //# sourceMappingURL=defaults.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.mjs","names":[],"sources":["../src/defaults.ts"],"sourcesContent":["import type { GrayMatterOptions, ResolvedOptions } from \"./types.ts\";\nimport { arrayify } from \"./utils.ts\";\n\n/**\n * Apply default options\n */\nexport function defaults(options?: GrayMatterOptions): ResolvedOptions {\n const delims = arrayify(options?.delimiters ?? \"---\");\n const delimiters: [string, string] =\n delims.length === 1 ? [delims[0]!, delims[0]!] : [delims[0]!, delims[1]!];\n\n return {\n ...options,\n delimiters,\n language: options?.language ?? \"yaml\",\n };\n}\n"],"mappings":";;;;;;AAMA,SAAgB,SAAS,SAA8C;CACrE,MAAM,SAAS,SAAS,SAAS,cAAc,MAAM;CACrD,MAAM,aACJ,OAAO,WAAW,IAAI,CAAC,OAAO,IAAK,OAAO,GAAI,GAAG,CAAC,OAAO,IAAK,OAAO,GAAI;AAE3E,QAAO;EACL,GAAG;EACH;EACA,UAAU,SAAS,YAAY;EAChC"}
@@ -0,0 +1,8 @@
1
+ //#region src/engines.d.ts
2
+ /**
3
+ * Built-in language names
4
+ */
5
+ type BuiltinLanguage = "yaml" | "json";
6
+ //#endregion
7
+ export { BuiltinLanguage };
8
+ //# sourceMappingURL=engines.d.mts.map
@@ -0,0 +1,63 @@
1
+ import { toRecord } from "./utils.mjs";
2
+ import { parse } from "./node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/parse.mjs";
3
+ import { stringify } from "./node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/stringify.mjs";
4
+ import "./node_modules/.pnpm/@jsr_std__yaml@1.0.10/node_modules/@jsr/std__yaml/mod.mjs";
5
+
6
+ //#region src/engines.ts
7
+ /**
8
+ * Array of built-in language names for runtime validation
9
+ */
10
+ const BUILTIN_LANGUAGES = ["yaml", "json"];
11
+ /**
12
+ * Check if value is a built-in language name
13
+ */
14
+ function isBuiltinLanguage(value) {
15
+ return typeof value === "string" && BUILTIN_LANGUAGES.includes(value);
16
+ }
17
+ /**
18
+ * Assert that value is a built-in language, returning the default if not
19
+ */
20
+ function toBuiltinLanguage(value, defaultLang = "yaml") {
21
+ return isBuiltinLanguage(value) ? value : defaultLang;
22
+ }
23
+ /**
24
+ * YAML engine using @std/yaml
25
+ */
26
+ const yaml = {
27
+ parse: (str) => {
28
+ return toRecord(parse(str));
29
+ },
30
+ stringify: (data) => {
31
+ return stringify(data);
32
+ }
33
+ };
34
+ /**
35
+ * JSON engine
36
+ */
37
+ const json = {
38
+ parse: (str) => {
39
+ return toRecord(JSON.parse(str));
40
+ },
41
+ stringify: (data, options) => {
42
+ const opts = {
43
+ replacer: null,
44
+ space: 2,
45
+ ...options
46
+ };
47
+ return JSON.stringify(data, opts.replacer, opts.space);
48
+ }
49
+ };
50
+ /**
51
+ * Get engine by language name
52
+ */
53
+ function getEngine(language) {
54
+ switch (language) {
55
+ case "yaml": return yaml;
56
+ case "json": return json;
57
+ default: throw new Error(`Unknown language: ${language}`);
58
+ }
59
+ }
60
+
61
+ //#endregion
62
+ export { getEngine, toBuiltinLanguage };
63
+ //# sourceMappingURL=engines.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engines.mjs","names":["yamlParse","yamlStringify"],"sources":["../src/engines.ts"],"sourcesContent":["import { parse as yamlParse, stringify as yamlStringify } from \"@std/yaml\";\nimport type { Engine, GrayMatterOptions } from \"./types.ts\";\nimport { toRecord } from \"./utils.ts\";\n\n/**\n * Built-in language names\n */\nexport type BuiltinLanguage = \"yaml\" | \"json\";\n\n/**\n * Array of built-in language names for runtime validation\n */\nconst BUILTIN_LANGUAGES = [\"yaml\", \"json\"] as const satisfies readonly BuiltinLanguage[];\n\n/**\n * Check if value is a built-in language name\n */\nfunction isBuiltinLanguage(value: unknown): value is BuiltinLanguage {\n return typeof value === \"string\" && BUILTIN_LANGUAGES.includes(value as BuiltinLanguage);\n}\n\n/**\n * Assert that value is a built-in language, returning the default if not\n */\nexport function toBuiltinLanguage(\n value: unknown,\n defaultLang: BuiltinLanguage = \"yaml\",\n): BuiltinLanguage {\n return isBuiltinLanguage(value) ? value : defaultLang;\n}\n\n/**\n * YAML engine using @std/yaml\n */\nconst yaml = {\n parse: (str: string): Record<string, unknown> => {\n return toRecord(yamlParse(str));\n },\n stringify: (data: Record<string, unknown>): string => {\n return yamlStringify(data);\n },\n} as const satisfies Engine;\n\n/**\n * JSON engine\n */\nconst json = {\n parse: (str: string): Record<string, unknown> => {\n return toRecord(JSON.parse(str));\n },\n stringify: (\n data: Record<string, unknown>,\n options?: GrayMatterOptions & { replacer?: null; space?: number },\n ): string => {\n const opts = { replacer: null, space: 2, ...options };\n return JSON.stringify(data, opts.replacer, opts.space);\n },\n} as const satisfies Engine;\n\n/**\n * Get engine by language name\n */\nexport function getEngine(language: BuiltinLanguage): Engine {\n switch (language) {\n case \"yaml\":\n return yaml;\n case \"json\":\n return json;\n default:\n throw new Error(`Unknown language: ${language satisfies never}`);\n }\n}\n\nif (import.meta.vitest) {\n const { fc, test } = await import(\"@fast-check/vitest\");\n\n /** Arbitrary for YAML-safe values (no undefined, functions, symbols) */\n const yamlSafeValue = fc.oneof(\n fc.string(),\n fc.integer(),\n fc.double({ noNaN: true, noDefaultInfinity: true }),\n fc.boolean(),\n fc.constant(null),\n );\n\n /** Arbitrary for simple YAML-compatible objects */\n const yamlSafeObject = fc.dictionary(\n fc.string({ minLength: 1, maxLength: 20 }).filter((s) => /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(s)),\n yamlSafeValue,\n { minKeys: 1, maxKeys: 5 },\n );\n\n describe(\"yaml engine\", () => {\n it(\"should parse simple YAML\", () => {\n const result = yaml.parse(\"title: Hello\\ncount: 42\");\n expect(result).toEqual({ title: \"Hello\", count: 42 });\n });\n\n it(\"should return empty object for null YAML\", () => {\n const result = yaml.parse(\"null\");\n expect(result).toEqual({});\n });\n\n it(\"should stringify object to YAML\", () => {\n const result = yaml.stringify({ title: \"Test\", count: 10 });\n expect(result).toContain(\"title: Test\");\n expect(result).toContain(\"count: 10\");\n });\n\n test.prop([yamlSafeObject])(\"should round-trip parse/stringify for safe objects\", (data) => {\n const stringified = yaml.stringify(data);\n const parsed = yaml.parse(stringified);\n expect(parsed).toEqual(data);\n });\n\n test.prop([fc.string({ minLength: 1, maxLength: 100 }), fc.integer()])(\n \"should preserve string and number types\",\n (str, num) => {\n const data = { str, num };\n const stringified = yaml.stringify(data);\n const parsed = yaml.parse(stringified);\n expect(parsed.str).toBe(str);\n expect(parsed.num).toBe(num);\n },\n );\n });\n\n describe(\"json engine\", () => {\n it(\"should parse JSON\", () => {\n const result = json.parse('{\"title\": \"Hello\", \"count\": 42}');\n expect(result).toEqual({ title: \"Hello\", count: 42 });\n });\n\n it(\"should stringify object to JSON\", () => {\n const result = json.stringify({ title: \"Test\" });\n expect(JSON.parse(result)).toEqual({ title: \"Test\" });\n });\n\n it(\"should throw on invalid JSON\", () => {\n expect(() => json.parse(\"not json\")).toThrow();\n });\n\n test.prop([\n fc.record({\n title: fc.string(),\n count: fc.integer(),\n active: fc.boolean(),\n }),\n ])(\"should round-trip parse/stringify\", (data) => {\n const stringified = json.stringify(data);\n const parsed = json.parse(stringified);\n expect(parsed).toEqual(data);\n });\n\n test.prop([\n fc.oneof(\n fc.string(),\n fc.integer(),\n fc.boolean(),\n fc.constant(null),\n fc.array(fc.oneof(fc.string(), fc.integer(), fc.boolean(), fc.constant(null))),\n ),\n ])(\"should handle common JSON values\", (value) => {\n const wrapped = { value };\n const stringified = json.stringify(wrapped);\n const parsed = json.parse(stringified);\n expect(parsed).toEqual(wrapped);\n });\n });\n\n describe(\"getEngine\", () => {\n it(\"should return yaml engine\", () => {\n expect(getEngine(\"yaml\")).toBe(yaml);\n });\n\n it(\"should return json engine\", () => {\n expect(getEngine(\"json\")).toBe(json);\n });\n });\n\n describe(\"isBuiltinLanguage\", () => {\n it(\"should return true for yaml\", () => {\n expect(isBuiltinLanguage(\"yaml\")).toBe(true);\n });\n\n it(\"should return true for json\", () => {\n expect(isBuiltinLanguage(\"json\")).toBe(true);\n });\n\n it(\"should return false for unknown languages\", () => {\n expect(isBuiltinLanguage(\"toml\")).toBe(false);\n expect(isBuiltinLanguage(\"\")).toBe(false);\n });\n\n it(\"should return false for non-strings\", () => {\n expect(isBuiltinLanguage(null)).toBe(false);\n expect(isBuiltinLanguage(undefined)).toBe(false);\n expect(isBuiltinLanguage(123)).toBe(false);\n });\n });\n\n describe(\"toBuiltinLanguage\", () => {\n it(\"should return valid language as-is\", () => {\n expect(toBuiltinLanguage(\"yaml\")).toBe(\"yaml\");\n expect(toBuiltinLanguage(\"json\")).toBe(\"json\");\n });\n\n it(\"should return default for invalid language\", () => {\n expect(toBuiltinLanguage(\"toml\")).toBe(\"yaml\");\n expect(toBuiltinLanguage(\"\")).toBe(\"yaml\");\n });\n\n it(\"should use custom default\", () => {\n expect(toBuiltinLanguage(\"toml\", \"json\")).toBe(\"json\");\n });\n });\n}\n"],"mappings":";;;;;;;;;AAYA,MAAM,oBAAoB,CAAC,QAAQ,OAAO;;;;AAK1C,SAAS,kBAAkB,OAA0C;AACnE,QAAO,OAAO,UAAU,YAAY,kBAAkB,SAAS,MAAyB;;;;;AAM1F,SAAgB,kBACd,OACA,cAA+B,QACd;AACjB,QAAO,kBAAkB,MAAM,GAAG,QAAQ;;;;;AAM5C,MAAM,OAAO;CACX,QAAQ,QAAyC;AAC/C,SAAO,SAASA,MAAU,IAAI,CAAC;;CAEjC,YAAY,SAA0C;AACpD,SAAOC,UAAc,KAAK;;CAE7B;;;;AAKD,MAAM,OAAO;CACX,QAAQ,QAAyC;AAC/C,SAAO,SAAS,KAAK,MAAM,IAAI,CAAC;;CAElC,YACE,MACA,YACW;EACX,MAAM,OAAO;GAAE,UAAU;GAAM,OAAO;GAAG,GAAG;GAAS;AACrD,SAAO,KAAK,UAAU,MAAM,KAAK,UAAU,KAAK,MAAM;;CAEzD;;;;AAKD,SAAgB,UAAU,UAAmC;AAC3D,SAAQ,UAAR;EACE,KAAK,OACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,QACE,OAAM,IAAI,MAAM,qBAAqB,WAA2B"}
@@ -0,0 +1,26 @@
1
+ import { getStringProp } from "./utils.mjs";
2
+ import { defaults } from "./defaults.mjs";
3
+
4
+ //#region src/excerpt.ts
5
+ /**
6
+ * Extract excerpt from file content
7
+ */
8
+ function excerpt(file, options) {
9
+ const opts = defaults(options);
10
+ file.data ??= {};
11
+ if (typeof opts.excerpt === "function") {
12
+ opts.excerpt(file, opts);
13
+ return file;
14
+ }
15
+ const dataSep = getStringProp(file.data, "excerpt_separator");
16
+ const sep = dataSep !== "" ? dataSep : opts.excerpt_separator;
17
+ if (sep == null && (opts.excerpt === false || opts.excerpt == null)) return file;
18
+ const delimiter = typeof opts.excerpt === "string" ? opts.excerpt : sep ?? opts.delimiters[0];
19
+ const idx = file.content.indexOf(delimiter);
20
+ if (idx !== -1) file.excerpt = file.content.slice(0, idx);
21
+ return file;
22
+ }
23
+
24
+ //#endregion
25
+ export { excerpt };
26
+ //# sourceMappingURL=excerpt.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"excerpt.mjs","names":[],"sources":["../src/excerpt.ts"],"sourcesContent":["import { defaults } from \"./defaults.ts\";\nimport type { GrayMatterFile, GrayMatterOptions } from \"./types.ts\";\nimport { getStringProp } from \"./utils.ts\";\n\n/**\n * Extract excerpt from file content\n */\nexport function excerpt(file: GrayMatterFile, options?: GrayMatterOptions): GrayMatterFile {\n const opts = defaults(options);\n\n // Ensure data is an object (defensive check for external callers)\n file.data ??= {};\n\n if (typeof opts.excerpt === \"function\") {\n opts.excerpt(file, opts);\n return file;\n }\n\n const dataSep = getStringProp(file.data, \"excerpt_separator\");\n const sep = dataSep !== \"\" ? dataSep : opts.excerpt_separator;\n\n if (sep == null && (opts.excerpt === false || opts.excerpt == null)) {\n return file;\n }\n\n const delimiter = typeof opts.excerpt === \"string\" ? opts.excerpt : (sep ?? opts.delimiters[0]);\n\n // if enabled, get the excerpt defined after front-matter\n const idx = file.content.indexOf(delimiter);\n if (idx !== -1) {\n file.excerpt = file.content.slice(0, idx);\n }\n\n return file;\n}\n\nif (import.meta.vitest) {\n const { fc, test } = await import(\"@fast-check/vitest\");\n const { Buffer } = await import(\"node:buffer\");\n\n const makeFile = (content: string, data: Record<string, unknown> = {}): GrayMatterFile => ({\n content,\n data,\n excerpt: \"\",\n orig: Buffer.from(content),\n language: \"yaml\",\n matter: \"\",\n isEmpty: false,\n stringify: () => \"\",\n });\n\n describe(\"excerpt\", () => {\n it(\"should extract excerpt using default delimiter\", () => {\n const file = makeFile(\"This is excerpt\\n---\\nThis is content\");\n const result = excerpt(file, { excerpt: true });\n expect(result.excerpt).toBe(\"This is excerpt\\n\");\n });\n\n it(\"should extract excerpt using custom string delimiter\", () => {\n const file = makeFile(\"This is excerpt\\n<!-- more -->\\nThis is content\");\n const result = excerpt(file, { excerpt: \"<!-- more -->\" });\n expect(result.excerpt).toBe(\"This is excerpt\\n\");\n });\n\n it(\"should use excerpt_separator from options\", () => {\n const file = makeFile(\"Excerpt here\\n***\\nContent here\");\n const result = excerpt(file, { excerpt: true, excerpt_separator: \"***\" });\n expect(result.excerpt).toBe(\"Excerpt here\\n\");\n });\n\n it(\"should use excerpt_separator from file.data\", () => {\n const file = makeFile(\"Excerpt\\n<!-- end -->\\nContent\", {\n excerpt_separator: \"<!-- end -->\",\n });\n const result = excerpt(file, { excerpt: true });\n expect(result.excerpt).toBe(\"Excerpt\\n\");\n });\n\n it(\"should not extract when excerpt is false\", () => {\n const file = makeFile(\"Some content\\n---\\nMore content\");\n const result = excerpt(file, { excerpt: false });\n expect(result.excerpt).toBe(\"\");\n });\n\n it(\"should not modify when delimiter not found\", () => {\n const file = makeFile(\"No delimiter here\");\n const result = excerpt(file, { excerpt: true });\n expect(result.excerpt).toBe(\"\");\n });\n\n it(\"should call custom excerpt function\", () => {\n const file = makeFile(\"Custom content\");\n const customFn = vi.fn((f: GrayMatterFile) => {\n f.excerpt = \"Custom excerpt\";\n });\n const result = excerpt(file, { excerpt: customFn });\n expect(customFn).toHaveBeenCalledOnce();\n expect(result.excerpt).toBe(\"Custom excerpt\");\n });\n\n it(\"should initialize file.data if null\", () => {\n const file = makeFile(\"content\");\n file.data = null as unknown as Record<string, unknown>;\n const result = excerpt(file, {});\n expect(result.data).toEqual({});\n });\n\n test.prop([\n fc.string({ minLength: 1, maxLength: 50 }),\n fc.string({ minLength: 1, maxLength: 50 }),\n ])(\"should extract correct excerpt when delimiter exists\", (excerptText, contentText) => {\n const delimiter = \"---\";\n const content = `${excerptText}\\n${delimiter}\\n${contentText}`;\n const file = makeFile(content);\n const result = excerpt(file, { excerpt: true });\n\n expect(result.excerpt).toBe(`${excerptText}\\n`);\n });\n\n test.prop([fc.string({ minLength: 1, maxLength: 100 })])(\n \"should not extract when no delimiter in content\",\n (content) => {\n const safeContent = content.replace(/---/g, \"___\");\n const file = makeFile(safeContent);\n const result = excerpt(file, { excerpt: true });\n expect(result.excerpt).toBe(\"\");\n },\n );\n\n test.prop([\n fc.string({ minLength: 1, maxLength: 20 }),\n fc.string({ minLength: 1, maxLength: 20 }),\n fc\n .string({ minLength: 1, maxLength: 10 })\n .filter((s) => !s.includes(\"\\n\") && !/[[\\]{}()*+?.,\\\\^$|#]/.test(s)),\n ])(\"should work with any custom delimiter\", (excerptText, contentText, customDelimiter) => {\n const safeExcerpt = excerptText.replaceAll(customDelimiter, \"\");\n if (safeExcerpt === \"\") return;\n const content = `${safeExcerpt}\\n${customDelimiter}\\n${contentText}`;\n const file = makeFile(content);\n const result = excerpt(file, { excerpt: customDelimiter });\n\n expect(result.excerpt).toBe(`${safeExcerpt}\\n`);\n });\n });\n}\n"],"mappings":";;;;;;;AAOA,SAAgB,QAAQ,MAAsB,SAA6C;CACzF,MAAM,OAAO,SAAS,QAAQ;AAG9B,MAAK,SAAS,EAAE;AAEhB,KAAI,OAAO,KAAK,YAAY,YAAY;AACtC,OAAK,QAAQ,MAAM,KAAK;AACxB,SAAO;;CAGT,MAAM,UAAU,cAAc,KAAK,MAAM,oBAAoB;CAC7D,MAAM,MAAM,YAAY,KAAK,UAAU,KAAK;AAE5C,KAAI,OAAO,SAAS,KAAK,YAAY,SAAS,KAAK,WAAW,MAC5D,QAAO;CAGT,MAAM,YAAY,OAAO,KAAK,YAAY,WAAW,KAAK,UAAW,OAAO,KAAK,WAAW;CAG5F,MAAM,MAAM,KAAK,QAAQ,QAAQ,UAAU;AAC3C,KAAI,QAAQ,GACV,MAAK,UAAU,KAAK,QAAQ,MAAM,GAAG,IAAI;AAG3C,QAAO"}
@@ -0,0 +1,12 @@
1
+ import { Engine, GrayMatterFile, GrayMatterInput, GrayMatterOptions, MatterFunction, ResolvedOptions } from "./types.mjs";
2
+ import { BuiltinLanguage } from "./engines.mjs";
3
+
4
+ //#region src/index.d.ts
5
+
6
+ /**
7
+ * The matter function with all static methods
8
+ */
9
+ declare const matter: MatterFunction;
10
+ //#endregion
11
+ export { type BuiltinLanguage, type Engine, type GrayMatterFile, type GrayMatterInput, type GrayMatterOptions, type MatterFunction, type ResolvedOptions, matter as default };
12
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,126 @@
1
+ import { defaults } from "./defaults.mjs";
2
+ import { toBuiltinLanguage } from "./engines.mjs";
3
+ import { excerpt } from "./excerpt.mjs";
4
+ import { parse } from "./parse.mjs";
5
+ import { stringify } from "./stringify.mjs";
6
+ import { toFile } from "./to-file.mjs";
7
+ import { readFileSync } from "node:fs";
8
+
9
+ //#region src/index.ts
10
+ /**
11
+ * Cache for parsed results
12
+ */
13
+ const cache = /* @__PURE__ */ new Map();
14
+ /**
15
+ * Takes a string or object with `content` property, extracts
16
+ * and parses front-matter from the string, then returns an object
17
+ * with `data`, `content` and other useful properties.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import matter from 'gray-matter-es';
22
+ * console.log(matter('---\ntitle: Home\n---\nOther stuff'));
23
+ * //=> { data: { title: 'Home'}, content: 'Other stuff' }
24
+ * ```
25
+ */
26
+ function matterImpl(input, options) {
27
+ if (input === "") return {
28
+ ...toFile(input),
29
+ isEmpty: true
30
+ };
31
+ let file = toFile(input);
32
+ const cached = cache.get(file.content);
33
+ if (!options) {
34
+ if (cached) {
35
+ file = { ...cached };
36
+ file.orig = cached.orig;
37
+ return file;
38
+ }
39
+ cache.set(file.content, file);
40
+ }
41
+ return parseMatter(file, options);
42
+ }
43
+ /**
44
+ * Parse front matter from file
45
+ */
46
+ function parseMatter(file, options) {
47
+ const opts = defaults(options);
48
+ const open = opts.delimiters[0];
49
+ const close = "\n" + opts.delimiters[1];
50
+ let str = file.content;
51
+ if (opts.language) file.language = opts.language;
52
+ const openLen = open.length;
53
+ if (!str.startsWith(open)) {
54
+ excerpt(file, opts);
55
+ return file;
56
+ }
57
+ if (str.at(openLen) === open.at(-1)) return file;
58
+ str = str.slice(openLen);
59
+ const len = str.length;
60
+ const lang = matterLanguage(str, opts);
61
+ if (lang.name) {
62
+ file.language = lang.name;
63
+ str = str.slice(lang.raw.length);
64
+ }
65
+ let closeIndex = str.indexOf(close);
66
+ if (closeIndex === -1) closeIndex = len;
67
+ file.matter = str.slice(0, closeIndex);
68
+ if (file.matter.replace(/^\s*#[^\n]+/gm, "").trim() === "") {
69
+ file.isEmpty = true;
70
+ file.empty = file.content;
71
+ file.data = {};
72
+ } else file.data = parse(toBuiltinLanguage(file.language), file.matter);
73
+ if (closeIndex === len) file.content = "";
74
+ else {
75
+ file.content = str.slice(closeIndex + close.length);
76
+ if (file.content.at(0) === "\r") file.content = file.content.slice(1);
77
+ if (file.content.at(0) === "\n") file.content = file.content.slice(1);
78
+ }
79
+ excerpt(file, opts);
80
+ return file;
81
+ }
82
+ /**
83
+ * Detect the language to use, if one is defined after the
84
+ * first front-matter delimiter.
85
+ */
86
+ function matterLanguage(str, options) {
87
+ const opts = defaults(options);
88
+ const open = opts.delimiters[0];
89
+ if (matterTest(str, opts)) str = str.slice(open.length);
90
+ const language = str.slice(0, str.search(/\r?\n/));
91
+ return {
92
+ raw: language,
93
+ name: language ? language.trim() : ""
94
+ };
95
+ }
96
+ /**
97
+ * Returns true if the given string has front matter.
98
+ */
99
+ function matterTest(str, options) {
100
+ return str.startsWith(defaults(options).delimiters[0]);
101
+ }
102
+ /**
103
+ * The matter function with all static methods
104
+ */
105
+ const matter = Object.assign(matterImpl, {
106
+ stringify: (file, data, options) => {
107
+ if (typeof file === "string") file = matterImpl(file, options);
108
+ return stringify(file, data, options);
109
+ },
110
+ read: (filepath, options) => {
111
+ const file = matterImpl(readFileSync(filepath, "utf8"), options);
112
+ file.path = filepath;
113
+ return file;
114
+ },
115
+ test: matterTest,
116
+ language: matterLanguage,
117
+ clearCache: () => {
118
+ cache.clear();
119
+ },
120
+ cache
121
+ });
122
+ var src_default = matter;
123
+
124
+ //#endregion
125
+ export { src_default as default };
126
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { defaults } from \"./defaults.ts\";\nimport { toBuiltinLanguage } from \"./engines.ts\";\nimport { excerpt } from \"./excerpt.ts\";\nimport { parse } from \"./parse.ts\";\nimport { stringify } from \"./stringify.ts\";\nimport { toFile } from \"./to-file.ts\";\nimport type {\n GrayMatterFile,\n GrayMatterInput,\n GrayMatterOptions,\n MatterFunction,\n} from \"./types.ts\";\n\nexport type {\n Engine,\n GrayMatterFile,\n GrayMatterInput,\n GrayMatterOptions,\n MatterFunction,\n ResolvedOptions,\n} from \"./types.ts\";\n\nexport type { BuiltinLanguage } from \"./engines.ts\";\n\n/**\n * Cache for parsed results\n */\nconst cache = new Map<string, GrayMatterFile>();\n\n/**\n * Takes a string or object with `content` property, extracts\n * and parses front-matter from the string, then returns an object\n * with `data`, `content` and other useful properties.\n *\n * @example\n * ```ts\n * import matter from 'gray-matter-es';\n * console.log(matter('---\\ntitle: Home\\n---\\nOther stuff'));\n * //=> { data: { title: 'Home'}, content: 'Other stuff' }\n * ```\n */\nfunction matterImpl(input: GrayMatterInput, options?: GrayMatterOptions): GrayMatterFile {\n if (input === \"\") {\n return { ...toFile(input), isEmpty: true };\n }\n\n let file = toFile(input);\n const cached = cache.get(file.content);\n\n if (!options) {\n if (cached) {\n file = { ...cached };\n file.orig = cached.orig;\n return file;\n }\n\n // only cache if there are no options passed\n cache.set(file.content, file);\n }\n\n return parseMatter(file, options);\n}\n\n/**\n * Parse front matter from file\n */\nfunction parseMatter(file: GrayMatterFile, options?: GrayMatterOptions): GrayMatterFile {\n const opts = defaults(options);\n const open = opts.delimiters[0];\n const close = \"\\n\" + opts.delimiters[1];\n let str = file.content;\n\n if (opts.language) {\n file.language = opts.language;\n }\n\n // get the length of the opening delimiter\n const openLen = open.length;\n if (!str.startsWith(open)) {\n excerpt(file, opts);\n return file;\n }\n\n // if the next character after the opening delimiter is\n // a character from the delimiter, then it's not a front-\n // matter delimiter\n if (str.at(openLen) === open.at(-1)) {\n return file;\n }\n\n // strip the opening delimiter\n str = str.slice(openLen);\n const len = str.length;\n\n // use the language defined after first delimiter, if it exists\n const lang = matterLanguage(str, opts);\n if (lang.name) {\n file.language = lang.name;\n str = str.slice(lang.raw.length);\n }\n\n // get the index of the closing delimiter\n let closeIndex = str.indexOf(close);\n if (closeIndex === -1) {\n closeIndex = len;\n }\n\n // get the raw front-matter block\n file.matter = str.slice(0, closeIndex);\n\n const block = file.matter.replace(/^\\s*#[^\\n]+/gm, \"\").trim();\n if (block === \"\") {\n file.isEmpty = true;\n file.empty = file.content;\n file.data = {};\n } else {\n // create file.data by parsing the raw file.matter block\n file.data = parse(toBuiltinLanguage(file.language), file.matter);\n }\n\n // update file.content\n if (closeIndex === len) {\n file.content = \"\";\n } else {\n file.content = str.slice(closeIndex + close.length);\n if (file.content.at(0) === \"\\r\") {\n file.content = file.content.slice(1);\n }\n if (file.content.at(0) === \"\\n\") {\n file.content = file.content.slice(1);\n }\n }\n\n excerpt(file, opts);\n return file;\n}\n\n/**\n * Detect the language to use, if one is defined after the\n * first front-matter delimiter.\n */\nfunction matterLanguage(str: string, options?: GrayMatterOptions): { raw: string; name: string } {\n const opts = defaults(options);\n const open = opts.delimiters[0];\n\n if (matterTest(str, opts)) {\n str = str.slice(open.length);\n }\n\n const language = str.slice(0, str.search(/\\r?\\n/));\n return {\n raw: language,\n name: language ? language.trim() : \"\",\n };\n}\n\n/**\n * Returns true if the given string has front matter.\n */\nfunction matterTest(str: string, options?: GrayMatterOptions): boolean {\n return str.startsWith(defaults(options).delimiters[0]);\n}\n\n/**\n * The matter function with all static methods\n */\nconst matter: MatterFunction = Object.assign(matterImpl, {\n /**\n * Stringify an object to YAML or the specified language, and\n * append it to the given string.\n */\n stringify: (\n file: GrayMatterFile | string,\n data?: Record<string, unknown>,\n options?: GrayMatterOptions,\n ): string => {\n if (typeof file === \"string\") file = matterImpl(file, options);\n return stringify(file, data, options);\n },\n\n /**\n * Synchronously read a file from the file system and parse front matter.\n */\n read: (filepath: string, options?: GrayMatterOptions): GrayMatterFile => {\n const str = readFileSync(filepath, \"utf8\");\n const file = matterImpl(str, options);\n file.path = filepath;\n return file;\n },\n\n /**\n * Returns true if the given string has front matter.\n */\n test: matterTest,\n\n /**\n * Detect the language to use, if one is defined after the\n * first front-matter delimiter.\n */\n language: matterLanguage,\n\n /**\n * Clear the cache\n */\n clearCache: (): void => {\n cache.clear();\n },\n\n /**\n * Expose cache (read-only access)\n */\n cache,\n});\n\nexport default matter;\n\nif (import.meta.vitest) {\n const { fc, test } = await import(\"@fast-check/vitest\");\n const { Buffer } = await import(\"node:buffer\");\n\n describe(\"matter\", () => {\n beforeEach(() => {\n matter.clearCache();\n });\n\n it(\"should extract YAML front matter\", () => {\n const actual = matter(\"---\\nabc: xyz\\n---\");\n expect(actual.data).toEqual({ abc: \"xyz\" });\n expect(actual.content).toBe(\"\");\n });\n\n it(\"should extract YAML front matter with content\", () => {\n const actual = matter(\"---\\nabc: xyz\\n---\\nfoo\");\n expect(actual.data).toEqual({ abc: \"xyz\" });\n expect(actual.content).toBe(\"foo\");\n });\n\n it(\"should return empty object for empty string\", () => {\n const actual = matter(\"\");\n expect(actual.data).toEqual({});\n expect(actual.content).toBe(\"\");\n });\n\n it(\"should return content when no front matter\", () => {\n const actual = matter(\"foo bar\");\n expect(actual.data).toEqual({});\n expect(actual.content).toBe(\"foo bar\");\n });\n\n it(\"should handle CRLF line endings\", () => {\n const actual = matter(\"---\\r\\nabc: xyz\\r\\n---\\r\\ncontent\");\n expect(actual.data).toEqual({ abc: \"xyz\" });\n expect(actual.content).toBe(\"content\");\n });\n\n it(\"should detect language after delimiter\", () => {\n const actual = matter('---json\\n{\"abc\": \"xyz\"}\\n---\\ncontent');\n expect(actual.data).toEqual({ abc: \"xyz\" });\n expect(actual.language).toBe(\"json\");\n });\n\n it(\"should handle custom delimiters\", () => {\n const actual = matter(\"~~~\\nabc: xyz\\n~~~\\ncontent\", {\n delimiters: \"~~~\",\n });\n expect(actual.data).toEqual({ abc: \"xyz\" });\n });\n\n it(\"should extract excerpt when enabled\", () => {\n const actual = matter(\"---\\nabc: xyz\\n---\\nexcerpt\\n---\\ncontent\", {\n excerpt: true,\n });\n expect(actual.excerpt).toBe(\"excerpt\\n\");\n });\n\n it(\"should use custom excerpt separator\", () => {\n const actual = matter(\"---\\nabc: xyz\\n---\\nexcerpt\\n<!-- more -->\\ncontent\", {\n excerpt: true,\n excerpt_separator: \"<!-- more -->\",\n });\n expect(actual.excerpt).toBe(\"excerpt\\n\");\n });\n\n it(\"should cache results when no options\", () => {\n const input = \"---\\nabc: xyz\\n---\";\n const first = matter(input);\n const second = matter(input);\n expect(first.data).toEqual(second.data);\n });\n\n it(\"should not cache when options provided\", () => {\n const input = \"---\\nabc: xyz\\n---\";\n matter(input);\n const second = matter(input, { language: \"yaml\" });\n expect(second.data).toEqual({ abc: \"xyz\" });\n });\n });\n\n describe(\"matter.stringify\", () => {\n it(\"should stringify data to YAML front matter\", () => {\n const result = matter.stringify(\"content\", { title: \"Hello\" });\n expect(result).toContain(\"---\");\n expect(result).toContain(\"title: Hello\");\n expect(result).toContain(\"content\");\n });\n\n it(\"should stringify file object\", () => {\n const file = matter(\"---\\ntitle: Test\\n---\\ncontent\");\n const result = matter.stringify(file, { title: \"Updated\" });\n expect(result).toContain(\"title: Updated\");\n });\n });\n\n describe(\"matter.test\", () => {\n it(\"should return true for string with front matter\", () => {\n expect(matter.test(\"---\\nabc: xyz\\n---\")).toBe(true);\n });\n\n it(\"should return false for string without front matter\", () => {\n expect(matter.test(\"foo bar\")).toBe(false);\n });\n });\n\n describe(\"matter.language\", () => {\n it(\"should detect language after delimiter\", () => {\n const result = matter.language(\"---json\\n{}\\n---\");\n expect(result.name).toBe(\"json\");\n });\n\n it(\"should return empty for no language\", () => {\n const result = matter.language(\"---\\nabc: xyz\\n---\");\n expect(result.name).toBe(\"\");\n });\n });\n\n describe(\"property-based tests\", () => {\n beforeEach(() => {\n matter.clearCache();\n });\n\n /** Arbitrary for YAML-safe keys */\n const yamlKey = fc\n .string({ minLength: 1, maxLength: 20 })\n .filter((s) => /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(s));\n\n /** Arbitrary for YAML-safe values */\n const yamlSafeValue = fc.oneof(\n fc.string({ maxLength: 50 }),\n fc.integer({ min: -10000, max: 10000 }),\n fc.boolean(),\n );\n\n /** Arbitrary for simple YAML-compatible objects */\n const yamlSafeObject = fc.dictionary(yamlKey, yamlSafeValue, { minKeys: 1, maxKeys: 5 });\n\n test.prop([fc.string({ minLength: 1, maxLength: 100 })])(\n \"Buffer and string input should produce equivalent results\",\n (content) => {\n matter.clearCache();\n const fromString = matter(content);\n matter.clearCache();\n const fromBuffer = matter(Buffer.from(content));\n\n expect(fromString.content).toBe(fromBuffer.content);\n expect(fromString.data).toEqual(fromBuffer.data);\n expect(fromString.excerpt).toBe(fromBuffer.excerpt);\n },\n );\n\n test.prop([yamlSafeObject, fc.string({ minLength: 0, maxLength: 50 })])(\n \"parse then stringify should preserve data\",\n (data, content) => {\n const frontMatter = Object.entries(data)\n .map(([k, v]) => `${k}: ${typeof v === \"string\" ? JSON.stringify(v) : v}`)\n .join(\"\\n\");\n const input = `---\\n${frontMatter}\\n---\\n${content}`;\n\n matter.clearCache();\n const parsed = matter(input);\n const stringified = matter.stringify(parsed);\n matter.clearCache();\n const reparsed = matter(stringified);\n\n expect(reparsed.data).toEqual(parsed.data);\n },\n );\n\n test.prop([fc.string({ minLength: 0, maxLength: 100 })])(\n \"content without front matter should be preserved\",\n (content) => {\n const safeContent = content.replace(/^---/gm, \"___\");\n matter.clearCache();\n const result = matter(safeContent);\n\n expect(result.content).toBe(safeContent);\n expect(result.data).toEqual({});\n },\n );\n\n test.prop([\n yamlSafeObject,\n fc.string({ minLength: 0, maxLength: 50 }),\n fc.string({ minLength: 1, maxLength: 50 }),\n ])(\"matter.test should correctly detect front matter\", (data, content, noFrontMatter) => {\n const frontMatter = Object.entries(data)\n .map(([k, v]) => `${k}: ${typeof v === \"string\" ? JSON.stringify(v) : v}`)\n .join(\"\\n\");\n const withFrontMatter = `---\\n${frontMatter}\\n---\\n${content}`;\n const withoutFrontMatter = noFrontMatter.replace(/^---/gm, \"___\");\n\n expect(matter.test(withFrontMatter)).toBe(true);\n expect(matter.test(withoutFrontMatter)).toBe(withoutFrontMatter.startsWith(\"---\"));\n });\n\n test.prop([fc.constantFrom(\"yaml\", \"json\"), yamlSafeObject, fc.string({ maxLength: 30 })])(\n \"should handle different languages\",\n (language, data, content) => {\n let frontMatterContent: string;\n if (language === \"json\") {\n frontMatterContent = JSON.stringify(data);\n } else {\n frontMatterContent = Object.entries(data)\n .map(([k, v]) => `${k}: ${typeof v === \"string\" ? JSON.stringify(v) : v}`)\n .join(\"\\n\");\n }\n\n const input = `---${language}\\n${frontMatterContent}\\n---\\n${content}`;\n matter.clearCache();\n const result = matter(input);\n\n expect(result.language).toBe(language);\n expect(result.data).toEqual(data);\n },\n );\n\n test.prop([fc.constantFrom(\"---\", \"~~~\", \"***\", \"+++\")])(\n \"should handle custom delimiters\",\n (delimiter) => {\n const input = `${delimiter}\\ntitle: Test\\n${delimiter}\\ncontent`;\n matter.clearCache();\n const result = matter(input, { delimiters: delimiter });\n\n expect(result.data).toEqual({ title: \"Test\" });\n expect(result.content).toBe(\"content\");\n },\n );\n\n test.prop([\n fc.string({ minLength: 1, maxLength: 20 }),\n fc.string({ minLength: 1, maxLength: 20 }),\n ])(\"should extract excerpt with custom separator\", (excerptText, contentText) => {\n const separator = \"<!-- more -->\";\n const safeExcerpt = excerptText.replace(separator, \"\");\n const safeContent = contentText.replace(separator, \"\");\n const input = `---\\ntitle: Test\\n---\\n${safeExcerpt}\\n${separator}\\n${safeContent}`;\n\n matter.clearCache();\n const result = matter(input, { excerpt: true, excerpt_separator: separator });\n\n expect(result.excerpt).toBe(`${safeExcerpt}\\n`);\n });\n\n test.prop([fc.string({ minLength: 0, maxLength: 50 })])(\n \"should handle CRLF and LF consistently\",\n (content) => {\n const yamlData = \"title: Test\";\n const inputLF = `---\\n${yamlData}\\n---\\n${content}`;\n const inputCRLF = `---\\r\\n${yamlData}\\r\\n---\\r\\n${content}`;\n\n matter.clearCache();\n const resultLF = matter(inputLF);\n matter.clearCache();\n const resultCRLF = matter(inputCRLF);\n\n expect(resultLF.data).toEqual(resultCRLF.data);\n expect(resultLF.content).toBe(resultCRLF.content);\n },\n );\n });\n}\n"],"mappings":";;;;;;;;;;;;AA4BA,MAAM,wBAAQ,IAAI,KAA6B;;;;;;;;;;;;;AAc/C,SAAS,WAAW,OAAwB,SAA6C;AACvF,KAAI,UAAU,GACZ,QAAO;EAAE,GAAG,OAAO,MAAM;EAAE,SAAS;EAAM;CAG5C,IAAI,OAAO,OAAO,MAAM;CACxB,MAAM,SAAS,MAAM,IAAI,KAAK,QAAQ;AAEtC,KAAI,CAAC,SAAS;AACZ,MAAI,QAAQ;AACV,UAAO,EAAE,GAAG,QAAQ;AACpB,QAAK,OAAO,OAAO;AACnB,UAAO;;AAIT,QAAM,IAAI,KAAK,SAAS,KAAK;;AAG/B,QAAO,YAAY,MAAM,QAAQ;;;;;AAMnC,SAAS,YAAY,MAAsB,SAA6C;CACtF,MAAM,OAAO,SAAS,QAAQ;CAC9B,MAAM,OAAO,KAAK,WAAW;CAC7B,MAAM,QAAQ,OAAO,KAAK,WAAW;CACrC,IAAI,MAAM,KAAK;AAEf,KAAI,KAAK,SACP,MAAK,WAAW,KAAK;CAIvB,MAAM,UAAU,KAAK;AACrB,KAAI,CAAC,IAAI,WAAW,KAAK,EAAE;AACzB,UAAQ,MAAM,KAAK;AACnB,SAAO;;AAMT,KAAI,IAAI,GAAG,QAAQ,KAAK,KAAK,GAAG,GAAG,CACjC,QAAO;AAIT,OAAM,IAAI,MAAM,QAAQ;CACxB,MAAM,MAAM,IAAI;CAGhB,MAAM,OAAO,eAAe,KAAK,KAAK;AACtC,KAAI,KAAK,MAAM;AACb,OAAK,WAAW,KAAK;AACrB,QAAM,IAAI,MAAM,KAAK,IAAI,OAAO;;CAIlC,IAAI,aAAa,IAAI,QAAQ,MAAM;AACnC,KAAI,eAAe,GACjB,cAAa;AAIf,MAAK,SAAS,IAAI,MAAM,GAAG,WAAW;AAGtC,KADc,KAAK,OAAO,QAAQ,iBAAiB,GAAG,CAAC,MAAM,KAC/C,IAAI;AAChB,OAAK,UAAU;AACf,OAAK,QAAQ,KAAK;AAClB,OAAK,OAAO,EAAE;OAGd,MAAK,OAAO,MAAM,kBAAkB,KAAK,SAAS,EAAE,KAAK,OAAO;AAIlE,KAAI,eAAe,IACjB,MAAK,UAAU;MACV;AACL,OAAK,UAAU,IAAI,MAAM,aAAa,MAAM,OAAO;AACnD,MAAI,KAAK,QAAQ,GAAG,EAAE,KAAK,KACzB,MAAK,UAAU,KAAK,QAAQ,MAAM,EAAE;AAEtC,MAAI,KAAK,QAAQ,GAAG,EAAE,KAAK,KACzB,MAAK,UAAU,KAAK,QAAQ,MAAM,EAAE;;AAIxC,SAAQ,MAAM,KAAK;AACnB,QAAO;;;;;;AAOT,SAAS,eAAe,KAAa,SAA4D;CAC/F,MAAM,OAAO,SAAS,QAAQ;CAC9B,MAAM,OAAO,KAAK,WAAW;AAE7B,KAAI,WAAW,KAAK,KAAK,CACvB,OAAM,IAAI,MAAM,KAAK,OAAO;CAG9B,MAAM,WAAW,IAAI,MAAM,GAAG,IAAI,OAAO,QAAQ,CAAC;AAClD,QAAO;EACL,KAAK;EACL,MAAM,WAAW,SAAS,MAAM,GAAG;EACpC;;;;;AAMH,SAAS,WAAW,KAAa,SAAsC;AACrE,QAAO,IAAI,WAAW,SAAS,QAAQ,CAAC,WAAW,GAAG;;;;;AAMxD,MAAM,SAAyB,OAAO,OAAO,YAAY;CAKvD,YACE,MACA,MACA,YACW;AACX,MAAI,OAAO,SAAS,SAAU,QAAO,WAAW,MAAM,QAAQ;AAC9D,SAAO,UAAU,MAAM,MAAM,QAAQ;;CAMvC,OAAO,UAAkB,YAAgD;EAEvE,MAAM,OAAO,WADD,aAAa,UAAU,OAAO,EACb,QAAQ;AACrC,OAAK,OAAO;AACZ,SAAO;;CAMT,MAAM;CAMN,UAAU;CAKV,kBAAwB;AACtB,QAAM,OAAO;;CAMf;CACD,CAAC;AAEF,kBAAe"}
@@ -0,0 +1,45 @@
1
+ //#region node_modules/.pnpm/@jsr+std__yaml@1.0.10/node_modules/@jsr/std__yaml/_chars.js
2
+ const BOM = 65279;
3
+ const TAB = 9;
4
+ const LINE_FEED = 10;
5
+ const CARRIAGE_RETURN = 13;
6
+ const SPACE = 32;
7
+ const EXCLAMATION = 33;
8
+ const DOUBLE_QUOTE = 34;
9
+ const SHARP = 35;
10
+ const PERCENT = 37;
11
+ const AMPERSAND = 38;
12
+ const SINGLE_QUOTE = 39;
13
+ const ASTERISK = 42;
14
+ const PLUS = 43;
15
+ const COMMA = 44;
16
+ const MINUS = 45;
17
+ const DOT = 46;
18
+ const COLON = 58;
19
+ const SMALLER_THAN = 60;
20
+ const GREATER_THAN = 62;
21
+ const QUESTION = 63;
22
+ const COMMERCIAL_AT = 64;
23
+ const LEFT_SQUARE_BRACKET = 91;
24
+ const BACKSLASH = 92;
25
+ const RIGHT_SQUARE_BRACKET = 93;
26
+ const GRAVE_ACCENT = 96;
27
+ const LEFT_CURLY_BRACKET = 123;
28
+ const VERTICAL_LINE = 124;
29
+ const RIGHT_CURLY_BRACKET = 125;
30
+ function isEOL(c) {
31
+ return c === LINE_FEED || c === CARRIAGE_RETURN;
32
+ }
33
+ function isWhiteSpace(c) {
34
+ return c === TAB || c === SPACE;
35
+ }
36
+ function isWhiteSpaceOrEOL(c) {
37
+ return isWhiteSpace(c) || isEOL(c);
38
+ }
39
+ function isFlowIndicator(c) {
40
+ return c === COMMA || c === LEFT_SQUARE_BRACKET || c === RIGHT_SQUARE_BRACKET || c === LEFT_CURLY_BRACKET || c === RIGHT_CURLY_BRACKET;
41
+ }
42
+
43
+ //#endregion
44
+ export { AMPERSAND, ASTERISK, BACKSLASH, BOM, CARRIAGE_RETURN, COLON, COMMA, COMMERCIAL_AT, DOT, DOUBLE_QUOTE, EXCLAMATION, GRAVE_ACCENT, GREATER_THAN, LEFT_CURLY_BRACKET, LEFT_SQUARE_BRACKET, LINE_FEED, MINUS, PERCENT, PLUS, QUESTION, RIGHT_CURLY_BRACKET, RIGHT_SQUARE_BRACKET, SHARP, SINGLE_QUOTE, SMALLER_THAN, SPACE, VERTICAL_LINE, isEOL, isFlowIndicator, isWhiteSpace, isWhiteSpaceOrEOL };
45
+ //# sourceMappingURL=_chars.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_chars.mjs","names":[],"sources":["../../../../../../../node_modules/.pnpm/@jsr+std__yaml@1.0.10/node_modules/@jsr/std__yaml/_chars.js"],"sourcesContent":["// Ported from js-yaml v3.13.1:\n// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da\n// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.\n// Copyright 2018-2025 the Deno authors. MIT license.\nexport const BOM = 0xfeff; /* BOM */ \nexport const TAB = 0x09; /* Tab */ \nexport const LINE_FEED = 0x0a; /* LF */ \nexport const CARRIAGE_RETURN = 0x0d; /* CR */ \nexport const SPACE = 0x20; /* Space */ \nexport const EXCLAMATION = 0x21; /* ! */ \nexport const DOUBLE_QUOTE = 0x22; /* \" */ \nexport const SHARP = 0x23; /* # */ \nexport const PERCENT = 0x25; /* % */ \nexport const AMPERSAND = 0x26; /* & */ \nexport const SINGLE_QUOTE = 0x27; /* ' */ \nexport const ASTERISK = 0x2a; /* * */ \nexport const PLUS = 0x2b; /* + */ \nexport const COMMA = 0x2c; /* , */ \nexport const MINUS = 0x2d; /* - */ \nexport const DOT = 0x2e; /* . */ \nexport const COLON = 0x3a; /* : */ \nexport const SMALLER_THAN = 0x3c; /* < */ \nexport const GREATER_THAN = 0x3e; /* > */ \nexport const QUESTION = 0x3f; /* ? */ \nexport const COMMERCIAL_AT = 0x40; /* @ */ \nexport const LEFT_SQUARE_BRACKET = 0x5b; /* [ */ \nexport const BACKSLASH = 0x5c; /* \\ */ \nexport const RIGHT_SQUARE_BRACKET = 0x5d; /* ] */ \nexport const GRAVE_ACCENT = 0x60; /* ` */ \nexport const LEFT_CURLY_BRACKET = 0x7b; /* { */ \nexport const VERTICAL_LINE = 0x7c; /* | */ \nexport const RIGHT_CURLY_BRACKET = 0x7d; /* } */ \nexport function isEOL(c) {\n return c === LINE_FEED || c === CARRIAGE_RETURN;\n}\nexport function isWhiteSpace(c) {\n return c === TAB || c === SPACE;\n}\nexport function isWhiteSpaceOrEOL(c) {\n return isWhiteSpace(c) || isEOL(c);\n}\nexport function isFlowIndicator(c) {\n return c === COMMA || c === LEFT_SQUARE_BRACKET || c === RIGHT_SQUARE_BRACKET || c === LEFT_CURLY_BRACKET || c === RIGHT_CURLY_BRACKET;\n}\n//# sourceMappingURL=_chars.js.map"],"x_google_ignoreList":[0],"mappings":";AAIA,MAAa,MAAM;AACnB,MAAa,MAAM;AACnB,MAAa,YAAY;AACzB,MAAa,kBAAkB;AAC/B,MAAa,QAAQ;AACrB,MAAa,cAAc;AAC3B,MAAa,eAAe;AAC5B,MAAa,QAAQ;AACrB,MAAa,UAAU;AACvB,MAAa,YAAY;AACzB,MAAa,eAAe;AAC5B,MAAa,WAAW;AACxB,MAAa,OAAO;AACpB,MAAa,QAAQ;AACrB,MAAa,QAAQ;AACrB,MAAa,MAAM;AACnB,MAAa,QAAQ;AACrB,MAAa,eAAe;AAC5B,MAAa,eAAe;AAC5B,MAAa,WAAW;AACxB,MAAa,gBAAgB;AAC7B,MAAa,sBAAsB;AACnC,MAAa,YAAY;AACzB,MAAa,uBAAuB;AACpC,MAAa,eAAe;AAC5B,MAAa,qBAAqB;AAClC,MAAa,gBAAgB;AAC7B,MAAa,sBAAsB;AACnC,SAAgB,MAAM,GAAG;AACvB,QAAO,MAAM,aAAa,MAAM;;AAElC,SAAgB,aAAa,GAAG;AAC9B,QAAO,MAAM,OAAO,MAAM;;AAE5B,SAAgB,kBAAkB,GAAG;AACnC,QAAO,aAAa,EAAE,IAAI,MAAM,EAAE;;AAEpC,SAAgB,gBAAgB,GAAG;AACjC,QAAO,MAAM,SAAS,MAAM,uBAAuB,MAAM,wBAAwB,MAAM,sBAAsB,MAAM"}