diff --git a/yaml/_dumper.ts b/yaml/_dumper_state.ts similarity index 98% rename from yaml/_dumper.ts rename to yaml/_dumper_state.ts index 34149d79a1a0..16e8ba794436 100644 --- a/yaml/_dumper.ts +++ b/yaml/_dumper_state.ts @@ -903,20 +903,3 @@ export class DumperState { this.usedDuplicates = new Set(); } } - -// deno-lint-ignore no-explicit-any -export function dump(input: any, options: DumperStateOptions = {}): string { - const state = new DumperState(options); - - if (state.useAnchors) state.getDuplicateReferences(input); - - if ( - state.writeNode(0, input, { - block: true, - compact: true, - isKey: false, - }) - ) return `${state.dump}\n`; - - return ""; -} diff --git a/yaml/_loader.ts b/yaml/_loader_state.ts similarity index 97% rename from yaml/_loader.ts rename to yaml/_loader_state.ts index 3020798afed8..37fb57af7a34 100644 --- a/yaml/_loader.ts +++ b/yaml/_loader_state.ts @@ -58,7 +58,7 @@ const PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i; const PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i; -interface LoaderStateOptions { +export interface LoaderStateOptions { /** specifies a schema to use. */ schema?: Schema; /** compatibility with JSON.parse behaviour. */ @@ -136,7 +136,7 @@ function codepointToChar(codepoint: number): string { ); } -class LoaderState { +export class LoaderState { schema: Schema; input: string; length: number; @@ -1691,42 +1691,3 @@ function composeNode( return state.tag !== null || state.anchor !== null || hasContent; } - -function sanitizeInput(input: string) { - input = String(input); - - if (input.length > 0) { - // Add tailing `\n` if not exists - if (!isEOL(input.charCodeAt(input.length - 1))) input += "\n"; - - // Strip BOM - if (input.charCodeAt(0) === 0xfeff) input = input.slice(1); - } - - // Use 0 as string terminator. That significantly simplifies bounds check. - input += "\0"; - - return input; -} - -export function loadDocuments( - input: string, - options: LoaderStateOptions = {}, -): unknown[] { - input = sanitizeInput(input); - const state = new LoaderState(input, options); - return [...state.readDocuments()]; -} - -export function load(input: string, options: LoaderStateOptions = {}): unknown { - input = sanitizeInput(input); - const state = new LoaderState(input, options); - const documentGenerator = state.readDocuments(); - const document = documentGenerator.next().value; - if (!documentGenerator.next().done) { - throw new SyntaxError( - "expected a single document in the stream, but found more", - ); - } - return document ?? null; -} diff --git a/yaml/parse.ts b/yaml/parse.ts index 1dab7a7c22ac..6e0729be1e0b 100644 --- a/yaml/parse.ts +++ b/yaml/parse.ts @@ -4,7 +4,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. -import { load, loadDocuments } from "./_loader.ts"; +import { isEOL } from "./_chars.ts"; +import { LoaderState } from "./_loader_state.ts"; import { SCHEMA_MAP, type SchemaType } from "./_schema.ts"; export type { SchemaType }; @@ -31,6 +32,23 @@ export interface ParseOptions { onWarning?(error: Error): void; } +function sanitizeInput(input: string) { + input = String(input); + + if (input.length > 0) { + // Add tailing `\n` if not exists + if (!isEOL(input.charCodeAt(input.length - 1))) input += "\n"; + + // Strip BOM + if (input.charCodeAt(0) === 0xfeff) input = input.slice(1); + } + + // Use 0 as string terminator. That significantly simplifies bounds check. + input += "\0"; + + return input; +} + /** * Parse and return a YAML string as a parsed YAML document object. * @@ -58,7 +76,19 @@ export function parse( content: string, options: ParseOptions = {}, ): unknown { - return load(content, { ...options, schema: SCHEMA_MAP.get(options.schema!) }); + content = sanitizeInput(content); + const state = new LoaderState(content, { + ...options, + schema: SCHEMA_MAP.get(options.schema!), + }); + const documentGenerator = state.readDocuments(); + const document = documentGenerator.next().value; + if (!documentGenerator.next().done) { + throw new SyntaxError( + "expected a single document in the stream, but found more", + ); + } + return document ?? null; } /** @@ -89,8 +119,10 @@ export function parse( * @returns Array of parsed documents. */ export function parseAll(content: string, options: ParseOptions = {}): unknown { - return loadDocuments(content, { + content = sanitizeInput(content); + const state = new LoaderState(content, { ...options, schema: SCHEMA_MAP.get(options.schema!), }); + return [...state.readDocuments()]; } diff --git a/yaml/stringify.ts b/yaml/stringify.ts index 8beaefd5c006..97f991f1a6de 100644 --- a/yaml/stringify.ts +++ b/yaml/stringify.ts @@ -4,7 +4,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // This module is browser compatible. -import { dump } from "./_dumper.ts"; +import { DumperState } from "./_dumper_state.ts"; import { SCHEMA_MAP, type SchemaType } from "./_schema.ts"; import type { StyleVariant } from "./_type.ts"; @@ -110,5 +110,16 @@ export function stringify( data: unknown, options: StringifyOptions = {}, ): string { - return dump(data, { ...options, schema: SCHEMA_MAP.get(options.schema!) }); + const state = new DumperState({ + ...options, + schema: SCHEMA_MAP.get(options.schema!), + }); + + // deno-lint-ignore no-explicit-any + if (state.useAnchors) state.getDuplicateReferences(data as any); + + if (state.writeNode(0, data, { block: true, compact: true, isKey: false })) { + return `${state.dump}\n`; + } + return ""; }