mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
refactor(front-matter): remove createExtractor()
(#5378)
Co-authored-by: Asher Gomez <ashersaupingomez@gmail.com>
This commit is contained in:
parent
faa633bd12
commit
a32c05fe26
@ -1,75 +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 { Extractor, Format } from "./_types.ts";
|
||||
import type { Extract } 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 {
|
||||
const [firstLine] = str.split(/(\r?\n)/) as [string];
|
||||
|
||||
for (const format of formats) {
|
||||
if (RECOGNIZE_REGEXP_MAP.get(format)?.test(firstLine)) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(`Unsupported front matter format.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (!parser) throw new TypeError(`Unsupported front matter format`);
|
||||
const regexp = EXTRACT_REGEXP_MAP.get(format);
|
||||
if (!regexp) throw new TypeError(`Unsupported front matter format`);
|
||||
|
||||
return _extract(str, regexp, parser);
|
||||
};
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { assertThrows } from "@std/assert";
|
||||
import { parse as parseYaml } from "@std/yaml/parse";
|
||||
import { parse as parseToml } from "@std/toml/parse";
|
||||
import {
|
||||
resolveTestDataPath,
|
||||
runExtractJsonTests,
|
||||
runExtractTomlTests,
|
||||
runExtractTypeErrorTests,
|
||||
runExtractYamlTests1,
|
||||
runExtractYamlTests2,
|
||||
} from "./_test_utils.ts";
|
||||
import { createExtractor, type Parser } from "./_create_extractor.ts";
|
||||
|
||||
const extractYaml = createExtractor({ "yaml": parseYaml as Parser });
|
||||
const extractToml = createExtractor({ "toml": parseToml as Parser });
|
||||
const extractJson = createExtractor({ "json": JSON.parse as Parser });
|
||||
const extractYamlOrJson = createExtractor({
|
||||
"yaml": parseYaml as Parser,
|
||||
"json": JSON.parse as Parser,
|
||||
});
|
||||
const extractAny = createExtractor({
|
||||
"yaml": parseYaml as Parser,
|
||||
"json": JSON.parse as Parser,
|
||||
"toml": parseToml as Parser,
|
||||
});
|
||||
|
||||
// YAML //
|
||||
|
||||
Deno.test("createExtractor() extracts yaml type error on invalid input", () => {
|
||||
runExtractTypeErrorTests("yaml", extractYaml);
|
||||
});
|
||||
|
||||
Deno.test("createExtractor() parses yaml delineate by `---`", async () => {
|
||||
await runExtractYamlTests1(extractYaml);
|
||||
});
|
||||
|
||||
Deno.test("createExtractor() parses yaml delineate by `---yaml`", async () => {
|
||||
await runExtractYamlTests2(extractYaml);
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name:
|
||||
"createExtractor() handles text between horizontal rules should not be recognized",
|
||||
async fn() {
|
||||
const str = await Deno.readTextFile(
|
||||
resolveTestDataPath("./horizontal_rules.md"),
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
() => {
|
||||
extractAny(str);
|
||||
},
|
||||
TypeError,
|
||||
"Unsupported front matter format",
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// JSON //
|
||||
|
||||
Deno.test("createExtractor() extracts json type error on invalid input", () => {
|
||||
runExtractTypeErrorTests("json", extractJson);
|
||||
});
|
||||
|
||||
Deno.test("createExtractor() parses json delineate by ---json", async () => {
|
||||
await runExtractJsonTests(extractJson);
|
||||
});
|
||||
|
||||
// TOML //
|
||||
|
||||
Deno.test("createExtractor() extracts toml type error on invalid input", () => {
|
||||
runExtractTypeErrorTests("toml", extractToml);
|
||||
});
|
||||
|
||||
Deno.test("createExtractor() parses toml delineate by ---toml", async () => {
|
||||
await runExtractTomlTests(extractToml);
|
||||
});
|
||||
|
||||
// MULTIPLE FORMATS //
|
||||
|
||||
Deno.test("createExtractor() parses yaml or json input", async () => {
|
||||
await runExtractYamlTests1(extractYamlOrJson);
|
||||
await runExtractYamlTests2(extractYamlOrJson);
|
||||
await runExtractJsonTests(extractYamlOrJson);
|
||||
});
|
||||
|
||||
Deno.test("createExtractor() parses any input", async () => {
|
||||
await runExtractYamlTests1(extractAny);
|
||||
await runExtractYamlTests2(extractAny);
|
||||
await runExtractJsonTests(extractAny);
|
||||
await runExtractTomlTests(extractAny);
|
||||
});
|
@ -27,21 +27,21 @@ function createRegExps(delimiters: Delimiter[]): [RegExp, RegExp] {
|
||||
];
|
||||
}
|
||||
|
||||
const [RECOGNIZE_YAML_REGEXP, EXTRACT_YAML_REGEXP] = createRegExps(
|
||||
export const [RECOGNIZE_YAML_REGEXP, EXTRACT_YAML_REGEXP] = createRegExps(
|
||||
[
|
||||
["---yaml", "---"],
|
||||
"= yaml =",
|
||||
"---",
|
||||
],
|
||||
);
|
||||
const [RECOGNIZE_TOML_REGEXP, EXTRACT_TOML_REGEXP] = createRegExps(
|
||||
export const [RECOGNIZE_TOML_REGEXP, EXTRACT_TOML_REGEXP] = createRegExps(
|
||||
[
|
||||
["---toml", "---"],
|
||||
"\\+\\+\\+",
|
||||
"= toml =",
|
||||
],
|
||||
);
|
||||
const [RECOGNIZE_JSON_REGEXP, EXTRACT_JSON_REGEXP] = createRegExps(
|
||||
export const [RECOGNIZE_JSON_REGEXP, EXTRACT_JSON_REGEXP] = createRegExps(
|
||||
[
|
||||
["---json", "---"],
|
||||
"= json =",
|
||||
|
44
front_matter/_shared.ts
Normal file
44
front_matter/_shared.ts
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { RECOGNIZE_REGEXP_MAP } from "./_formats.ts";
|
||||
import type { Format } from "./_types.ts";
|
||||
import type { Extract } from "./types.ts";
|
||||
|
||||
/** Parser function type */
|
||||
export type Parser = <T = Record<string, unknown>>(str: string) => T;
|
||||
|
||||
export function extractAndParse<T>(
|
||||
input: string,
|
||||
extractRegExp: RegExp,
|
||||
parse: Parser,
|
||||
): Extract<T> {
|
||||
const match = extractRegExp.exec(input);
|
||||
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 = input.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.
|
||||
*/
|
||||
export function recognize(
|
||||
str: string,
|
||||
formats: Format[],
|
||||
): Format {
|
||||
const [firstLine] = str.split(/(\r?\n)/) as [string];
|
||||
|
||||
for (const format of formats) {
|
||||
if (RECOGNIZE_REGEXP_MAP.get(format)?.test(firstLine)) return format;
|
||||
}
|
||||
|
||||
throw new TypeError(`Unsupported front matter format.`);
|
||||
}
|
@ -1,17 +1,24 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { createExtractor, type Parser } from "./_create_extractor.ts";
|
||||
import { extractAndParse, type Parser, recognize } from "./_shared.ts";
|
||||
import { parse as parseYaml } from "@std/yaml/parse";
|
||||
import { parse as parseToml } from "@std/toml/parse";
|
||||
import type { Extract } from "./types.ts";
|
||||
import type { Format } from "./test.ts";
|
||||
import { EXTRACT_REGEXP_MAP } from "./_formats.ts";
|
||||
|
||||
export type { Extract };
|
||||
|
||||
const _extractor = createExtractor({
|
||||
yaml: parseYaml as Parser,
|
||||
toml: parseToml as Parser,
|
||||
json: JSON.parse as Parser,
|
||||
});
|
||||
function getParserForFormat(format: Format): Parser {
|
||||
switch (format) {
|
||||
case "yaml":
|
||||
return parseYaml as Parser;
|
||||
case "toml":
|
||||
return parseToml as Parser;
|
||||
case "json":
|
||||
return JSON.parse;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts and parses {@link https://yaml.org | YAML}, {@link https://toml.io |
|
||||
@ -40,5 +47,9 @@ const _extractor = createExtractor({
|
||||
* @returns The extracted front matter and body content.
|
||||
*/
|
||||
export function extract<T>(text: string): Extract<T> {
|
||||
return _extractor(text);
|
||||
const formats = [...EXTRACT_REGEXP_MAP.keys()] as Format[];
|
||||
const format = recognize(text, formats);
|
||||
const regexp = EXTRACT_REGEXP_MAP.get(format) as RegExp;
|
||||
const parser = getParserForFormat(format);
|
||||
return extractAndParse(text, regexp, parser);
|
||||
}
|
||||
|
@ -1,14 +1,11 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { createExtractor, type Parser } from "./_create_extractor.ts";
|
||||
import { extractAndParse } from "./_shared.ts";
|
||||
import { EXTRACT_JSON_REGEXP } from "./_formats.ts";
|
||||
import type { Extract } from "./types.ts";
|
||||
|
||||
export type { Extract };
|
||||
|
||||
const _extractor = createExtractor({
|
||||
["json"]: JSON.parse as Parser,
|
||||
});
|
||||
|
||||
/**
|
||||
* Extracts and parses {@link https://www.json.org/ | JSON } from the metadata
|
||||
* of front matter content.
|
||||
@ -36,5 +33,5 @@ const _extractor = createExtractor({
|
||||
* @returns The extracted JSON front matter and body content.
|
||||
*/
|
||||
export function extract<T>(text: string): Extract<T> {
|
||||
return _extractor(text);
|
||||
return extractAndParse(text, EXTRACT_JSON_REGEXP, JSON.parse);
|
||||
}
|
||||
|
@ -1,15 +1,12 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { createExtractor, type Parser } from "./_create_extractor.ts";
|
||||
import { extractAndParse, type Parser } from "./_shared.ts";
|
||||
import { parse } from "@std/toml/parse";
|
||||
import type { Extract } from "./types.ts";
|
||||
import { EXTRACT_TOML_REGEXP } from "./_formats.ts";
|
||||
|
||||
export type { Extract };
|
||||
|
||||
const _extractor = createExtractor({
|
||||
["toml"]: parse as Parser,
|
||||
});
|
||||
|
||||
/**
|
||||
* Extracts and parses {@link https://toml.io | TOML} from the metadata of
|
||||
* front matter content.
|
||||
@ -37,5 +34,5 @@ const _extractor = createExtractor({
|
||||
* @returns The extracted TOML front matter and body content.
|
||||
*/
|
||||
export function extract<T>(text: string): Extract<T> {
|
||||
return _extractor(text);
|
||||
return extractAndParse(text, EXTRACT_TOML_REGEXP, parse as Parser);
|
||||
}
|
||||
|
@ -1,15 +1,12 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { createExtractor, type Parser } from "./_create_extractor.ts";
|
||||
import { extractAndParse, type Parser } from "./_shared.ts";
|
||||
import { parse } from "@std/yaml/parse";
|
||||
import type { Extract } from "./types.ts";
|
||||
import { EXTRACT_YAML_REGEXP } from "./_formats.ts";
|
||||
|
||||
export type { Extract };
|
||||
|
||||
const _extractor = createExtractor({
|
||||
["yaml"]: parse as Parser,
|
||||
});
|
||||
|
||||
/**
|
||||
* Extracts and parses {@link https://yaml.org | YAML} from the metadata of
|
||||
* front matter content.
|
||||
@ -37,5 +34,5 @@ const _extractor = createExtractor({
|
||||
* @returns The extracted YAML front matter and body content.
|
||||
*/
|
||||
export function extract<T>(text: string): Extract<T> {
|
||||
return _extractor(text);
|
||||
return extractAndParse(text, EXTRACT_YAML_REGEXP, parse as Parser);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user