BREAKING(front-matter): remove createExtractor() (#5266)

* BREAKING(front-matter): remove `createExtractor()`

* work
This commit is contained in:
Asher Gomez 2024-07-03 17:53:55 +10:00 committed by GitHub
parent 769cc02d71
commit ebf8eeec59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 119 additions and 218 deletions

View File

@ -0,0 +1,83 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { EXTRACT_REGEXP_MAP, RECOGNIZE_REGEXP_MAP } from "./_formats.ts";
import type { Format } from "./_types.ts";
import type { Extract, Extractor } from "./types.ts";
/** Parser function type used alongside {@linkcode createExtractor}. */
export type Parser = <T = Record<string, unknown>>(str: string) => T;
function _extract<T>(
str: string,
rx: RegExp,
parse: Parser,
): Extract<T> {
const match = rx.exec(str);
if (!match || match.index !== 0) {
throw new TypeError("Unexpected end of input");
}
const frontMatter = match.at(-1)?.replace(/^\s+|\s+$/g, "") || "";
const attrs = parse(frontMatter) as T;
const body = str.replace(match[0], "");
return { frontMatter, body, attrs };
}
/**
* Recognizes the format of the front matter in a string.
* Supports {@link https://yaml.org | YAML}, {@link https://toml.io | TOML} and
* {@link https://www.json.org/ | JSON}.
*
* @param str String to recognize.
* @param formats A list of formats to recognize. Defaults to all supported formats.
*/
function recognize(str: string, formats?: Format[]): Format {
if (!formats) {
formats = Object.keys(RECOGNIZE_REGEXP_MAP) as Format[];
}
const [firstLine] = str.split(/(\r?\n)/) as [string];
for (const format of formats) {
if (format === "unknown") {
continue;
}
if (RECOGNIZE_REGEXP_MAP[format].test(firstLine)) {
return format;
}
}
return "unknown";
}
/**
* Factory that creates a function that extracts front matter from a string with
* the given parsers. Supports {@link https://yaml.org | YAML},
* {@link https://toml.io | TOML} and {@link https://www.json.org/ | JSON}.
*
* For simple use cases where you know which format to parse in advance, use the
* pre-built extractors:
*
* - {@linkcode https://jsr.io/@std/front-matter/doc/yaml/~/extract | extractYaml}
* - {@linkcode https://jsr.io/@std/front-matter/doc/toml/~/extract | extractToml}
* - {@linkcode https://jsr.io/@std/front-matter/doc/json/~/extract | extractJson}
*
* @param formats A descriptor containing Format-parser pairs to use for each format.
* @returns A function that extracts front matter from a string with the given parsers.
*/
export function createExtractor(
formats: Partial<Record<Format, Parser>>,
): Extractor {
const formatKeys = Object.keys(formats) as Format[];
return function extract<T>(str: string): Extract<T> {
const format = recognize(str, formatKeys);
const parser = formats[format];
if (format === "unknown" || !parser) {
throw new TypeError(`Unsupported front matter format`);
}
return _extract(str, EXTRACT_REGEXP_MAP[format], parser);
};
}

View File

@ -11,7 +11,7 @@ import {
runExtractYamlTests1,
runExtractYamlTests2,
} from "./_test_utils.ts";
import { createExtractor, type Parser } from "./create_extractor.ts";
import { createExtractor, type Parser } from "./_create_extractor.ts";
const extractYaml = createExtractor({ "yaml": parseYaml as Parser });
const extractToml = createExtractor({ "toml": parseToml as Parser });

View File

@ -1,12 +1,11 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import {
createExtractor,
type Extractor,
type Parser,
} from "./create_extractor.ts";
import { createExtractor, type Parser } from "./_create_extractor.ts";
import { parse as parseYaml } from "@std/yaml/parse";
import { parse as parseToml } from "@std/toml/parse";
import type { Extractor } from "./types.ts";
export type { Extractor };
/**
* Extracts and parses {@link https://yaml.org | YAML}, {@link https://toml.io |

View File

@ -1,174 +0,0 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { EXTRACT_REGEXP_MAP, RECOGNIZE_REGEXP_MAP } from "./_formats.ts";
import type { Format } from "./_types.ts";
/** Return type for {@linkcode Extractor}. */
export type Extract<T> = {
frontMatter: string;
body: string;
attrs: T;
};
/** Function return type for {@linkcode createExtractor}. */
export type Extractor = <T = Record<string, unknown>>(
str: string,
) => Extract<T>;
/** Parser function type used alongside {@linkcode createExtractor}. */
export type Parser = <T = Record<string, unknown>>(str: string) => T;
function _extract<T>(
str: string,
rx: RegExp,
parse: Parser,
): Extract<T> {
const match = rx.exec(str);
if (!match || match.index !== 0) {
throw new TypeError("Unexpected end of input");
}
const frontMatter = match.at(-1)?.replace(/^\s+|\s+$/g, "") || "";
const attrs = parse(frontMatter) as T;
const body = str.replace(match[0], "");
return { frontMatter, body, attrs };
}
/**
* Recognizes the format of the front matter in a string.
* Supports {@link https://yaml.org | YAML}, {@link https://toml.io | TOML} and
* {@link https://www.json.org/ | JSON}.
*
* @param str String to recognize.
* @param formats A list of formats to recognize. Defaults to all supported formats.
*/
function recognize(str: string, formats?: Format[]): Format {
if (!formats) {
formats = Object.keys(RECOGNIZE_REGEXP_MAP) as Format[];
}
const [firstLine] = str.split(/(\r?\n)/) as [string];
for (const format of formats) {
if (format === "unknown") {
continue;
}
if (RECOGNIZE_REGEXP_MAP[format].test(firstLine)) {
return format;
}
}
return "unknown";
}
/**
* Factory that creates a function that extracts front matter from a string with
* the given parsers. Supports {@link https://yaml.org | YAML},
* {@link https://toml.io | TOML} and {@link https://www.json.org/ | JSON}.
*
* For simple use cases where you know which format to parse in advance, use the
* pre-built extractors:
*
* - {@linkcode https://jsr.io/@std/front-matter/doc/yaml/~/extract | extractYaml}
* - {@linkcode https://jsr.io/@std/front-matter/doc/toml/~/extract | extractToml}
* - {@linkcode https://jsr.io/@std/front-matter/doc/json/~/extract | extractJson}
*
* @param formats A descriptor containing Format-parser pairs to use for each format.
* @returns A function that extracts front matter from a string with the given parsers.
*
* @example Extract YAML front matter
* ```ts
* import { createExtractor, Parser } from "@std/front-matter";
* import { assertEquals } from "@std/assert";
* import { parse as parseYaml } from "@std/yaml/parse";
*
* const extractYaml = createExtractor({ yaml: parseYaml as Parser });
* const { attrs, body, frontMatter } = extractYaml<{ title: string }>(
* `---
* title: Three dashes marks the spot
* ---
* ferret`);
* assertEquals(attrs.title, "Three dashes marks the spot");
* assertEquals(body, "ferret");
* assertEquals(frontMatter, "title: Three dashes marks the spot");
* ```
*
* @example Extract TOML front matter
* ```ts
* import { createExtractor, Parser } from "@std/front-matter";
* import { assertEquals } from "@std/assert";
* import { parse as parseToml } from "@std/toml/parse";
*
* const extractToml = createExtractor({ toml: parseToml as Parser });
* const { attrs, body, frontMatter } = extractToml<{ title: string }>(
* `---toml
* title = 'Three dashes followed by format marks the spot'
* ---
* `);
* assertEquals(attrs.title, "Three dashes followed by format marks the spot");
* assertEquals(body, "");
* assertEquals(frontMatter, "title = 'Three dashes followed by format marks the spot'");
* ```
*
* @example Extract JSON front matter
* ```ts
* import { createExtractor, Parser } from "@std/front-matter";
* import { assertEquals } from "@std/assert";
*
* const extractJson = createExtractor({ json: JSON.parse as Parser });
* const { attrs, body, frontMatter } = extractJson<{ title: string }>(
* `---json
* {"title": "Three dashes followed by format marks the spot"}
* ---
* goat`);
* assertEquals(attrs.title, "Three dashes followed by format marks the spot");
* assertEquals(body, "goat");
* assertEquals(frontMatter, `{"title": "Three dashes followed by format marks the spot"}`);
* ```
*
* @example Extract YAML or JSON front matter
* ```ts
* import { createExtractor, Parser } from "@std/front-matter";
* import { assertEquals } from "@std/assert";
* import { parse as parseYaml } from "@std/yaml/parse";
*
* const extractYamlOrJson = createExtractor({
* yaml: parseYaml as Parser,
* json: JSON.parse as Parser,
* });
*
* let { attrs, body, frontMatter } = extractYamlOrJson<{ title: string }>(
* `---
* title: Three dashes marks the spot
* ---
* ferret`);
* assertEquals(attrs.title, "Three dashes marks the spot");
* assertEquals(body, "ferret");
* assertEquals(frontMatter, "title: Three dashes marks the spot");
*
* ({ attrs, body, frontMatter } = extractYamlOrJson<{ title: string }>(
* `---json
* {"title": "Three dashes followed by format marks the spot"}
* ---
* goat`));
* assertEquals(attrs.title, "Three dashes followed by format marks the spot");
* assertEquals(body, "goat");
* assertEquals(frontMatter, `{"title": "Three dashes followed by format marks the spot"}`);
* ```
*/
export function createExtractor(
formats: Partial<Record<Format, Parser>>,
): Extractor {
const formatKeys = Object.keys(formats) as Format[];
return function extract<T>(str: string): Extract<T> {
const format = recognize(str, formatKeys);
const parser = formats[format];
if (format === "unknown" || !parser) {
throw new TypeError(`Unsupported front matter format`);
}
return _extract(str, EXTRACT_REGEXP_MAP[format], parser);
};
}

View File

@ -4,10 +4,10 @@
"exports": {
".": "./mod.ts",
"./any": "./any.ts",
"./create-extractor": "./create_extractor.ts",
"./json": "./json.ts",
"./test": "./test.ts",
"./toml": "./toml.ts",
"./yaml": "./yaml.ts"
"./yaml": "./yaml.ts",
"./types": "./types.ts"
}
}

View File

@ -1,34 +1,13 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import {
createExtractor,
type Extractor,
type Parser,
} from "./create_extractor.ts";
import { createExtractor, type Parser } from "./_create_extractor.ts";
import type { Extractor } from "./types.ts";
export type { Extractor };
/**
* Extracts and parses {@link https://www.json.org/ | JSON } from the metadata
* of front matter content.
*
* @example Extract JSON front matter
* ```ts
* import { extract } from "@std/front-matter/json";
* import { assertEquals } from "@std/assert";
*
* const output = `---json
* {
* "title": "Three dashes marks the spot"
* }
* ---
* Hello, world!`;
* const result = extract(output);
*
* assertEquals(result, {
* frontMatter: '{\n "title": "Three dashes marks the spot"\n}',
* body: "Hello, world!",
* attrs: { title: "Three dashes marks the spot" },
* });
* ```
*/
export const extract: Extractor = createExtractor({
json: JSON.parse as Parser,

View File

@ -124,7 +124,7 @@ import { extract as extractJson } from "./json.ts";
import { extract as extractToml } from "./toml.ts";
import { extract as extractYaml } from "./yaml.ts";
export * from "./create_extractor.ts";
export * from "./test.ts";
export * from "./types.ts";
export { extractJson, extractToml, extractYaml };

View File

@ -1,11 +1,10 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import {
createExtractor,
type Extractor,
type Parser,
} from "./create_extractor.ts";
import { createExtractor, type Parser } from "./_create_extractor.ts";
import { parse } from "@std/toml/parse";
import type { Extractor } from "./types.ts";
export type { Extractor };
/**
* Extracts and parses {@link https://toml.io | TOML} from the metadata of

16
front_matter/types.ts Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
/** Return type for functions of the {@linkcode Extractor} type. */
export type Extract<T> = {
frontMatter: string;
body: string;
attrs: T;
};
/**
* Type for function that accepts an input string and returns
* {@linkcode Extract}.
*/
export type Extractor = <T = Record<string, unknown>>(
str: string,
) => Extract<T>;

View File

@ -1,11 +1,10 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import {
createExtractor,
type Extractor,
type Parser,
} from "./create_extractor.ts";
import { createExtractor, type Parser } from "./_create_extractor.ts";
import { parse } from "@std/yaml/parse";
import type { Extractor } from "./types.ts";
export type { Extractor };
/**
* Extracts and parses {@link https://yaml.org | YAML} from the metadata of