Skip to main content
Module

std/yaml/_loader/loader.ts

Deno standard library
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800
// Ported from js-yaml v3.13.1:// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { YAMLError } from "../_error.ts";import { Mark } from "../_mark.ts";import type { Type } from "../type.ts";import * as common from "../_utils.ts";import { LoaderState, LoaderStateOptions, ResultType } from "./loader_state.ts";
type Any = common.Any;type ArrayObject<T = Any> = common.ArrayObject<T>;
const { hasOwn } = Object;
const CONTEXT_FLOW_IN = 1;const CONTEXT_FLOW_OUT = 2;const CONTEXT_BLOCK_IN = 3;const CONTEXT_BLOCK_OUT = 4;
const CHOMPING_CLIP = 1;const CHOMPING_STRIP = 2;const CHOMPING_KEEP = 3;
const PATTERN_NON_PRINTABLE = // deno-lint-ignore no-control-regex /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/;const PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/;const PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/;const PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i;const PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;
function _class(obj: unknown): string { return Object.prototype.toString.call(obj);}
function isEOL(c: number): boolean { return c === 0x0a || /* LF */ c === 0x0d /* CR */;}
function isWhiteSpace(c: number): boolean { return c === 0x09 || /* Tab */ c === 0x20 /* Space */;}
function isWsOrEol(c: number): boolean { return ( c === 0x09 /* Tab */ || c === 0x20 /* Space */ || c === 0x0a /* LF */ || c === 0x0d /* CR */ );}
function isFlowIndicator(c: number): boolean { return ( c === 0x2c /* , */ || c === 0x5b /* [ */ || c === 0x5d /* ] */ || c === 0x7b /* { */ || c === 0x7d /* } */ );}
function fromHexCode(c: number): number { if (0x30 <= /* 0 */ c && c <= 0x39 /* 9 */) { return c - 0x30; }
const lc = c | 0x20;
if (0x61 <= /* a */ lc && lc <= 0x66 /* f */) { return lc - 0x61 + 10; }
return -1;}
function escapedHexLen(c: number): number { if (c === 0x78 /* x */) { return 2; } if (c === 0x75 /* u */) { return 4; } if (c === 0x55 /* U */) { return 8; } return 0;}
function fromDecimalCode(c: number): number { if (0x30 <= /* 0 */ c && c <= 0x39 /* 9 */) { return c - 0x30; }
return -1;}
function simpleEscapeSequence(c: number): string { return c === 0x30 /* 0 */ ? "\x00" : c === 0x61 /* a */ ? "\x07" : c === 0x62 /* b */ ? "\x08" : c === 0x74 /* t */ ? "\x09" : c === 0x09 /* Tab */ ? "\x09" : c === 0x6e /* n */ ? "\x0A" : c === 0x76 /* v */ ? "\x0B" : c === 0x66 /* f */ ? "\x0C" : c === 0x72 /* r */ ? "\x0D" : c === 0x65 /* e */ ? "\x1B" : c === 0x20 /* Space */ ? " " : c === 0x22 /* " */ ? "\x22" : c === 0x2f /* / */ ? "/" : c === 0x5c /* \ */ ? "\x5C" : c === 0x4e /* N */ ? "\x85" : c === 0x5f /* _ */ ? "\xA0" : c === 0x4c /* L */ ? "\u2028" : c === 0x50 /* P */ ? "\u2029" : "";}
function charFromCodepoint(c: number): string { if (c <= 0xffff) { return String.fromCharCode(c); } // Encode UTF-16 surrogate pair // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF return String.fromCharCode( ((c - 0x010000) >> 10) + 0xd800, ((c - 0x010000) & 0x03ff) + 0xdc00, );}
const simpleEscapeCheck = Array.from<number>({ length: 256 }); // integer, for fast accessconst simpleEscapeMap = Array.from<string>({ length: 256 });for (let i = 0; i < 256; i++) { simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0; simpleEscapeMap[i] = simpleEscapeSequence(i);}
function generateError(state: LoaderState, message: string): YAMLError { return new YAMLError( message, new Mark( state.filename as string, state.input, state.position, state.line, state.position - state.lineStart, ), );}
function throwError(state: LoaderState, message: string): never { throw generateError(state, message);}
function throwWarning(state: LoaderState, message: string) { if (state.onWarning) { state.onWarning.call(null, generateError(state, message)); }}
interface DirectiveHandlers { [directive: string]: ( state: LoaderState, name: string, ...args: string[] ) => void;}
const directiveHandlers: DirectiveHandlers = { YAML(state, _name, ...args: string[]) { if (state.version !== null) { return throwError(state, "duplication of %YAML directive"); }
if (args.length !== 1) { return throwError(state, "YAML directive accepts exactly one argument"); }
const match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]); if (match === null) { return throwError(state, "ill-formed argument of the YAML directive"); }
const major = parseInt(match[1], 10); const minor = parseInt(match[2], 10); if (major !== 1) { return throwError(state, "unacceptable YAML version of the document"); }
state.version = args[0]; state.checkLineBreaks = minor < 2; if (minor !== 1 && minor !== 2) { return throwWarning(state, "unsupported YAML version of the document"); } },
TAG(state, _name, ...args: string[]) { if (args.length !== 2) { return throwError(state, "TAG directive accepts exactly two arguments"); }
const handle = args[0]; const prefix = args[1];
if (!PATTERN_TAG_HANDLE.test(handle)) { return throwError( state, "ill-formed tag handle (first argument) of the TAG directive", ); }
if (state.tagMap && hasOwn(state.tagMap, handle)) { return throwError( state, `there is a previously declared suffix for "${handle}" tag handle`, ); }
if (!PATTERN_TAG_URI.test(prefix)) { return throwError( state, "ill-formed tag prefix (second argument) of the TAG directive", ); }
if (typeof state.tagMap === "undefined") { state.tagMap = Object.create(null) as common.ArrayObject; } state.tagMap[handle] = prefix; },};
function captureSegment( state: LoaderState, start: number, end: number, checkJson: boolean,) { let result: string; if (start < end) { result = state.input.slice(start, end);
if (checkJson) { for ( let position = 0, length = result.length; position < length; position++ ) { const character = result.charCodeAt(position); if ( !(character === 0x09 || (0x20 <= character && character <= 0x10ffff)) ) { return throwError(state, "expected valid JSON character"); } } } else if (PATTERN_NON_PRINTABLE.test(result)) { return throwError(state, "the stream contains non-printable characters"); }
state.result += result; }}
function mergeMappings( state: LoaderState, destination: ArrayObject, source: ArrayObject, overridableKeys: ArrayObject<boolean>,) { if (!common.isObject(source)) { return throwError( state, "cannot merge mappings; the provided source object is unacceptable", ); }
const keys = Object.keys(source); for (let i = 0, len = keys.length; i < len; i++) { const key = keys[i]; if (!hasOwn(destination, key)) { Object.defineProperty(destination, key, { value: source[key], writable: true, enumerable: true, configurable: true, }); overridableKeys[key] = true; } }}
function storeMappingPair( state: LoaderState, result: ArrayObject | null, overridableKeys: ArrayObject<boolean>, keyTag: string | null, keyNode: Any, valueNode: unknown, startLine?: number, startPos?: number,): ArrayObject { // The output is a plain object here, so keys can only be strings. // We need to convert keyNode to a string, but doing so can hang the process // (deeply nested arrays that explode exponentially using aliases). if (Array.isArray(keyNode)) { keyNode = Array.prototype.slice.call(keyNode);
for (let index = 0, quantity = keyNode.length; index < quantity; index++) { if (Array.isArray(keyNode[index])) { return throwError(state, "nested arrays are not supported inside keys"); }
if ( typeof keyNode === "object" && _class(keyNode[index]) === "[object Object]" ) { keyNode[index] = "[object Object]"; } } }
// Avoid code execution in load() via toString property // (still use its own toString for arrays, timestamps, // and whatever user schema extensions happen to have @@toStringTag) if (typeof keyNode === "object" && _class(keyNode) === "[object Object]") { keyNode = "[object Object]"; }
keyNode = String(keyNode);
if (result === null) { result = {}; }
if (keyTag === "tag:yaml.org,2002:merge") { if (Array.isArray(valueNode)) { for ( let index = 0, quantity = valueNode.length; index < quantity; index++ ) { mergeMappings(state, result, valueNode[index], overridableKeys); } } else { mergeMappings(state, result, valueNode as ArrayObject, overridableKeys); } } else { if ( !state.json && !hasOwn(overridableKeys, keyNode) && hasOwn(result, keyNode) ) { state.line = startLine || state.line; state.position = startPos || state.position; return throwError(state, "duplicated mapping key"); } Object.defineProperty(result, keyNode, { value: valueNode, writable: true, enumerable: true, configurable: true, }); delete overridableKeys[keyNode]; }
return result;}
function readLineBreak(state: LoaderState) { const ch = state.input.charCodeAt(state.position);
if (ch === 0x0a /* LF */) { state.position++; } else if (ch === 0x0d /* CR */) { state.position++; if (state.input.charCodeAt(state.position) === 0x0a /* LF */) { state.position++; } } else { return throwError(state, "a line break is expected"); }
state.line += 1; state.lineStart = state.position;}
function skipSeparationSpace( state: LoaderState, allowComments: boolean, checkIndent: number,): number { let lineBreaks = 0, ch = state.input.charCodeAt(state.position);
while (ch !== 0) { while (isWhiteSpace(ch)) { ch = state.input.charCodeAt(++state.position); }
if (allowComments && ch === 0x23 /* # */) { do { ch = state.input.charCodeAt(++state.position); } while (ch !== 0x0a && /* LF */ ch !== 0x0d && /* CR */ ch !== 0); }
if (isEOL(ch)) { readLineBreak(state);
ch = state.input.charCodeAt(state.position); lineBreaks++; state.lineIndent = 0;
while (ch === 0x20 /* Space */) { state.lineIndent++; ch = state.input.charCodeAt(++state.position); } } else { break; } }
if ( checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent ) { throwWarning(state, "deficient indentation"); }
return lineBreaks;}
function testDocumentSeparator(state: LoaderState): boolean { let _position = state.position; let ch = state.input.charCodeAt(_position);
// Condition state.position === state.lineStart is tested // in parent on each call, for efficiency. No needs to test here again. if ( (ch === 0x2d || /* - */ ch === 0x2e) /* . */ && ch === state.input.charCodeAt(_position + 1) && ch === state.input.charCodeAt(_position + 2) ) { _position += 3;
ch = state.input.charCodeAt(_position);
if (ch === 0 || isWsOrEol(ch)) { return true; } }
return false;}
function writeFoldedLines(state: LoaderState, count: number) { if (count === 1) { state.result += " "; } else if (count > 1) { state.result += common.repeat("\n", count - 1); }}
function readPlainScalar( state: LoaderState, nodeIndent: number, withinFlowCollection: boolean,): boolean { const kind = state.kind; const result = state.result; let ch = state.input.charCodeAt(state.position);
if ( isWsOrEol(ch) || isFlowIndicator(ch) || ch === 0x23 /* # */ || ch === 0x26 /* & */ || ch === 0x2a /* * */ || ch === 0x21 /* ! */ || ch === 0x7c /* | */ || ch === 0x3e /* > */ || ch === 0x27 /* ' */ || ch === 0x22 /* " */ || ch === 0x25 /* % */ || ch === 0x40 /* @ */ || ch === 0x60 /* ` */ ) { return false; }
let following: number; if (ch === 0x3f || /* ? */ ch === 0x2d /* - */) { following = state.input.charCodeAt(state.position + 1);
if ( isWsOrEol(following) || (withinFlowCollection && isFlowIndicator(following)) ) { return false; } }
state.kind = "scalar"; state.result = ""; let captureEnd: number, captureStart = (captureEnd = state.position); let hasPendingContent = false; let line = 0; while (ch !== 0) { if (ch === 0x3a /* : */) { following = state.input.charCodeAt(state.position + 1);
if ( isWsOrEol(following) || (withinFlowCollection && isFlowIndicator(following)) ) { break; } } else if (ch === 0x23 /* # */) { const preceding = state.input.charCodeAt(state.position - 1);
if (isWsOrEol(preceding)) { break; } } else if ( (state.position === state.lineStart && testDocumentSeparator(state)) || (withinFlowCollection && isFlowIndicator(ch)) ) { break; } else if (isEOL(ch)) { line = state.line; const lineStart = state.lineStart; const lineIndent = state.lineIndent; skipSeparationSpace(state, false, -1);
if (state.lineIndent >= nodeIndent) { hasPendingContent = true; ch = state.input.charCodeAt(state.position); continue; } else { state.position = captureEnd; state.line = line; state.lineStart = lineStart; state.lineIndent = lineIndent; break; } }
if (hasPendingContent) { captureSegment(state, captureStart, captureEnd, false); writeFoldedLines(state, state.line - line); captureStart = captureEnd = state.position; hasPendingContent = false; }
if (!isWhiteSpace(ch)) { captureEnd = state.position + 1; }
ch = state.input.charCodeAt(++state.position); }
captureSegment(state, captureStart, captureEnd, false);
if (state.result) { return true; }
state.kind = kind; state.result = result; return false;}
function readSingleQuotedScalar( state: LoaderState, nodeIndent: number,): boolean { let ch, captureStart, captureEnd;
ch = state.input.charCodeAt(state.position);
if (ch !== 0x27 /* ' */) { return false; }
state.kind = "scalar"; state.result = ""; state.position++; captureStart = captureEnd = state.position;
while ((ch = state.input.charCodeAt(state.position)) !== 0) { if (ch === 0x27 /* ' */) { captureSegment(state, captureStart, state.position, true); ch = state.input.charCodeAt(++state.position);
if (ch === 0x27 /* ' */) { captureStart = state.position; state.position++; captureEnd = state.position; } else { return true; } } else if (isEOL(ch)) { captureSegment(state, captureStart, captureEnd, true); writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); captureStart = captureEnd = state.position; } else if ( state.position === state.lineStart && testDocumentSeparator(state) ) { return throwError( state, "unexpected end of the document within a single quoted scalar", ); } else { state.position++; captureEnd = state.position; } }
return throwError( state, "unexpected end of the stream within a single quoted scalar", );}
function readDoubleQuotedScalar( state: LoaderState, nodeIndent: number,): boolean { let ch = state.input.charCodeAt(state.position);
if (ch !== 0x22 /* " */) { return false; }
state.kind = "scalar"; state.result = ""; state.position++; let captureEnd: number, captureStart = (captureEnd = state.position); let tmp: number; while ((ch = state.input.charCodeAt(state.position)) !== 0) { if (ch === 0x22 /* " */) { captureSegment(state, captureStart, state.position, true); state.position++; return true; } if (ch === 0x5c /* \ */) { captureSegment(state, captureStart, state.position, true); ch = state.input.charCodeAt(++state.position);
if (isEOL(ch)) { skipSeparationSpace(state, false, nodeIndent);
// TODO(bartlomieju): rework to inline fn with no type cast? } else if (ch < 256 && simpleEscapeCheck[ch]) { state.result += simpleEscapeMap[ch]; state.position++; } else if ((tmp = escapedHexLen(ch)) > 0) { let hexLength = tmp; let hexResult = 0;
for (; hexLength > 0; hexLength--) { ch = state.input.charCodeAt(++state.position);
if ((tmp = fromHexCode(ch)) >= 0) { hexResult = (hexResult << 4) + tmp; } else { return throwError(state, "expected hexadecimal character"); } }
state.result += charFromCodepoint(hexResult);
state.position++; } else { return throwError(state, "unknown escape sequence"); }
captureStart = captureEnd = state.position; } else if (isEOL(ch)) { captureSegment(state, captureStart, captureEnd, true); writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); captureStart = captureEnd = state.position; } else if ( state.position === state.lineStart && testDocumentSeparator(state) ) { return throwError( state, "unexpected end of the document within a double quoted scalar", ); } else { state.position++; captureEnd = state.position; } }
return throwError( state, "unexpected end of the stream within a double quoted scalar", );}
function readFlowCollection(state: LoaderState, nodeIndent: number): boolean { let ch = state.input.charCodeAt(state.position); let terminator: number; let isMapping = true; let result: ResultType = {}; if (ch === 0x5b /* [ */) { terminator = 0x5d; /* ] */ isMapping = false; result = []; } else if (ch === 0x7b /* { */) { terminator = 0x7d; /* } */ } else { return false; }
if ( state.anchor !== null && typeof state.anchor != "undefined" && typeof state.anchorMap != "undefined" ) { state.anchorMap[state.anchor] = result; }
ch = state.input.charCodeAt(++state.position);
const tag = state.tag, anchor = state.anchor; let readNext = true; let valueNode, keyNode, keyTag: string | null = (keyNode = valueNode = null), isExplicitPair: boolean, isPair = (isExplicitPair = false); let following = 0, line = 0; const overridableKeys: ArrayObject<boolean> = Object.create(null); while (ch !== 0) { skipSeparationSpace(state, true, nodeIndent);
ch = state.input.charCodeAt(state.position);
if (ch === terminator) { state.position++; state.tag = tag; state.anchor = anchor; state.kind = isMapping ? "mapping" : "sequence"; state.result = result; return true; } if (!readNext) { return throwError(state, "missed comma between flow collection entries"); }
keyTag = keyNode = valueNode = null; isPair = isExplicitPair = false;
if (ch === 0x3f /* ? */) { following = state.input.charCodeAt(state.position + 1);
if (isWsOrEol(following)) { isPair = isExplicitPair = true; state.position++; skipSeparationSpace(state, true, nodeIndent); } }
line = state.line; composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); keyTag = state.tag || null; keyNode = state.result; skipSeparationSpace(state, true, nodeIndent);
ch = state.input.charCodeAt(state.position);
if ((isExplicitPair || state.line === line) && ch === 0x3a /* : */) { isPair = true; ch = state.input.charCodeAt(++state.position); skipSeparationSpace(state, true, nodeIndent); composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); valueNode = state.result; }
if (isMapping) { storeMappingPair( state, result, overridableKeys, keyTag, keyNode, valueNode, ); } else if (isPair) { (result as ArrayObject[]).push( storeMappingPair( state, null, overridableKeys, keyTag, keyNode, valueNode, ), ); } else { (result as ResultType[]).push(keyNode as ResultType); }
skipSeparationSpace(state, true, nodeIndent);
ch = state.input.charCodeAt(state.position);
if (ch === 0x2c /* , */) { readNext = true; ch = state.input.charCodeAt(++state.position); } else { readNext = false; } }
return throwError( state, "unexpected end of the stream within a flow collection", );}
function readBlockScalar(state: LoaderState, nodeIndent: number): boolean { let chomping = CHOMPING_CLIP, didReadContent = false, detectedIndent = false, textIndent = nodeIndent, emptyLines = 0, atMoreIndented = false;
let ch = state.input.charCodeAt(state.position);
let folding = false; if (ch === 0x7c /* | */) { folding = false; } else if (ch === 0x3e /* > */) { folding = true; } else { return false; }
state.kind = "scalar"; state.result = "";
let tmp = 0; while (ch !== 0) { ch = state.input.charCodeAt(++state.position);
if (ch === 0x2b || /* + */ ch === 0x2d /* - */) { if (CHOMPING_CLIP === chomping) { chomping = ch === 0x2b /* + */ ? CHOMPING_KEEP : CHOMPING_STRIP; } else { return throwError(state, "repeat of a chomping mode identifier"); } } else if ((tmp = fromDecimalCode(ch)) >= 0) { if (tmp === 0) { return throwError( state, "bad explicit indentation width of a block scalar; it cannot be less than one", ); } else if (!detectedIndent) { textIndent = nodeIndent + tmp - 1; detectedIndent = true; } else { return throwError(state, "repeat of an indentation width identifier"); } } else { break; } }
if (isWhiteSpace(ch)) { do { ch = state.input.charCodeAt(++state.position); } while (isWhiteSpace(ch));
if (ch === 0x23 /* # */) { do { ch = state.input.charCodeAt(++state.position); } while (!isEOL(ch) && ch !== 0); } }
while (ch !== 0) { readLineBreak(state); state.lineIndent = 0;
ch = state.input.charCodeAt(state.position);
while ( (!detectedIndent || state.lineIndent < textIndent) && ch === 0x20 /* Space */ ) { state.lineIndent++; ch = state.input.charCodeAt(++state.position); }
if (!detectedIndent && state.lineIndent > textIndent) { textIndent = state.lineIndent; }
if (isEOL(ch)) { emptyLines++; continue; }
// End of the scalar. if (state.lineIndent < textIndent) { // Perform the chomping. if (chomping === CHOMPING_KEEP) { state.result += common.repeat( "\n", didReadContent ? 1 + emptyLines : emptyLines, ); } else if (chomping === CHOMPING_CLIP) { if (didReadContent) { // i.e. only if the scalar is not empty. state.result += "\n"; } }
// Break this `while` cycle and go to the function's epilogue. break; }
// Folded style: use fancy rules to handle line breaks. if (folding) { // Lines starting with white space characters (more-indented lines) are not folded. if (isWhiteSpace(ch)) { atMoreIndented = true; // except for the first content line (cf. Example 8.1) state.result += common.repeat( "\n", didReadContent ? 1 + emptyLines : emptyLines, );
// End of more-indented block. } else if (atMoreIndented) { atMoreIndented = false; state.result += common.repeat("\n", emptyLines + 1);
// Just one line break - perceive as the same line. } else if (emptyLines === 0) { if (didReadContent) { // i.e. only if we have already read some scalar content. state.result += " "; }
// Several line breaks - perceive as different lines. } else { state.result += common.repeat("\n", emptyLines); }
// Literal style: just add exact number of line breaks between content lines. } else { // Keep all line breaks except the header line break. state.result += common.repeat( "\n", didReadContent ? 1 + emptyLines : emptyLines, ); }
didReadContent = true; detectedIndent = true; emptyLines = 0; const captureStart = state.position;
while (!isEOL(ch) && ch !== 0) { ch = state.input.charCodeAt(++state.position); }
captureSegment(state, captureStart, state.position, false); }
return true;}
function readBlockSequence(state: LoaderState, nodeIndent: number): boolean { let line: number, following: number, detected = false, ch: number; const tag = state.tag, anchor = state.anchor, result: unknown[] = [];
if ( state.anchor !== null && typeof state.anchor !== "undefined" && typeof state.anchorMap !== "undefined" ) { state.anchorMap[state.anchor] = result; }
ch = state.input.charCodeAt(state.position);
while (ch !== 0) { if (ch !== 0x2d /* - */) { break; }
following = state.input.charCodeAt(state.position + 1);
if (!isWsOrEol(following)) { break; }
detected = true; state.position++;
if (skipSeparationSpace(state, true, -1)) { if (state.lineIndent <= nodeIndent) { result.push(null); ch = state.input.charCodeAt(state.position); continue; } }
line = state.line; composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true); result.push(state.result); skipSeparationSpace(state, true, -1);
ch = state.input.charCodeAt(state.position);
if ((state.line === line || state.lineIndent > nodeIndent) && ch !== 0) { return throwError(state, "bad indentation of a sequence entry"); } else if (state.lineIndent < nodeIndent) { break; } }
if (detected) { state.tag = tag; state.anchor = anchor; state.kind = "sequence"; state.result = result; return true; } return false;}
function readBlockMapping( state: LoaderState, nodeIndent: number, flowIndent: number,): boolean { const tag = state.tag, anchor = state.anchor, result = {}, overridableKeys = Object.create(null); let following: number, allowCompact = false, line: number, pos: number, keyTag = null, keyNode = null, valueNode = null, atExplicitKey = false, detected = false, ch: number;
if ( state.anchor !== null && typeof state.anchor !== "undefined" && typeof state.anchorMap !== "undefined" ) { state.anchorMap[state.anchor] = result; }
ch = state.input.charCodeAt(state.position);
while (ch !== 0) { following = state.input.charCodeAt(state.position + 1); line = state.line; // Save the current line. pos = state.position;
// // Explicit notation case. There are two separate blocks: // first for the key (denoted by "?") and second for the value (denoted by ":") // if ((ch === 0x3f || /* ? */ ch === 0x3a) && /* : */ isWsOrEol(following)) { if (ch === 0x3f /* ? */) { if (atExplicitKey) { storeMappingPair( state, result, overridableKeys, keyTag as string, keyNode, null, ); keyTag = keyNode = valueNode = null; }
detected = true; atExplicitKey = true; allowCompact = true; } else if (atExplicitKey) { // i.e. 0x3A/* : */ === character after the explicit key. atExplicitKey = false; allowCompact = true; } else { return throwError( state, "incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line", ); }
state.position += 1; ch = following;
// // Implicit notation case. Flow-style node as the key first, then ":", and the value. // } else if (composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) { if (state.line === line) { ch = state.input.charCodeAt(state.position);
while (isWhiteSpace(ch)) { ch = state.input.charCodeAt(++state.position); }
if (ch === 0x3a /* : */) { ch = state.input.charCodeAt(++state.position);
if (!isWsOrEol(ch)) { return throwError( state, "a whitespace character is expected after the key-value separator within a block mapping", ); }
if (atExplicitKey) { storeMappingPair( state, result, overridableKeys, keyTag as string, keyNode, null, ); keyTag = keyNode = valueNode = null; }
detected = true; atExplicitKey = false; allowCompact = false; keyTag = state.tag; keyNode = state.result; } else if (detected) { return throwError( state, "can not read an implicit mapping pair; a colon is missed", ); } else { state.tag = tag; state.anchor = anchor; return true; // Keep the result of `composeNode`. } } else if (detected) { return throwError( state, "can not read a block mapping entry; a multiline key may not be an implicit key", ); } else { state.tag = tag; state.anchor = anchor; return true; // Keep the result of `composeNode`. } } else { break; // Reading is done. Go to the epilogue. }
// // Common reading code for both explicit and implicit notations. // if (state.line === line || state.lineIndent > nodeIndent) { if ( composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact) ) { if (atExplicitKey) { keyNode = state.result; } else { valueNode = state.result; } }
if (!atExplicitKey) { storeMappingPair( state, result, overridableKeys, keyTag as string, keyNode, valueNode, line, pos, ); keyTag = keyNode = valueNode = null; }
skipSeparationSpace(state, true, -1); ch = state.input.charCodeAt(state.position); }
if (state.lineIndent > nodeIndent && ch !== 0) { return throwError(state, "bad indentation of a mapping entry"); } else if (state.lineIndent < nodeIndent) { break; } }
// // Epilogue. //
// Special case: last mapping's node contains only the key in explicit notation. if (atExplicitKey) { storeMappingPair( state, result, overridableKeys, keyTag as string, keyNode, null, ); }
// Expose the resulting mapping. if (detected) { state.tag = tag; state.anchor = anchor; state.kind = "mapping"; state.result = result; }
return detected;}
function readTagProperty(state: LoaderState): boolean { let position: number, isVerbatim = false, isNamed = false, tagHandle = "", tagName: string, ch: number;
ch = state.input.charCodeAt(state.position);
if (ch !== 0x21 /* ! */) return false;
if (state.tag !== null) { return throwError(state, "duplication of a tag property"); }
ch = state.input.charCodeAt(++state.position);
if (ch === 0x3c /* < */) { isVerbatim = true; ch = state.input.charCodeAt(++state.position); } else if (ch === 0x21 /* ! */) { isNamed = true; tagHandle = "!!"; ch = state.input.charCodeAt(++state.position); } else { tagHandle = "!"; }
position = state.position;
if (isVerbatim) { do { ch = state.input.charCodeAt(++state.position); } while (ch !== 0 && ch !== 0x3e /* > */);
if (state.position < state.length) { tagName = state.input.slice(position, state.position); ch = state.input.charCodeAt(++state.position); } else { return throwError( state, "unexpected end of the stream within a verbatim tag", ); } } else { while (ch !== 0 && !isWsOrEol(ch)) { if (ch === 0x21 /* ! */) { if (!isNamed) { tagHandle = state.input.slice(position - 1, state.position + 1);
if (!PATTERN_TAG_HANDLE.test(tagHandle)) { return throwError( state, "named tag handle cannot contain such characters", ); }
isNamed = true; position = state.position + 1; } else { return throwError( state, "tag suffix cannot contain exclamation marks", ); } }
ch = state.input.charCodeAt(++state.position); }
tagName = state.input.slice(position, state.position);
if (PATTERN_FLOW_INDICATORS.test(tagName)) { return throwError( state, "tag suffix cannot contain flow indicator characters", ); } }
if (tagName && !PATTERN_TAG_URI.test(tagName)) { return throwError( state, `tag name cannot contain such characters: ${tagName}`, ); }
if (isVerbatim) { state.tag = tagName; } else if ( typeof state.tagMap !== "undefined" && hasOwn(state.tagMap, tagHandle) ) { state.tag = state.tagMap[tagHandle] + tagName; } else if (tagHandle === "!") { state.tag = `!${tagName}`; } else if (tagHandle === "!!") { state.tag = `tag:yaml.org,2002:${tagName}`; } else { return throwError(state, `undeclared tag handle "${tagHandle}"`); }
return true;}
function readAnchorProperty(state: LoaderState): boolean { let ch = state.input.charCodeAt(state.position); if (ch !== 0x26 /* & */) return false;
if (state.anchor !== null) { return throwError(state, "duplication of an anchor property"); } ch = state.input.charCodeAt(++state.position);
const position = state.position; while (ch !== 0 && !isWsOrEol(ch) && !isFlowIndicator(ch)) { ch = state.input.charCodeAt(++state.position); }
if (state.position === position) { return throwError( state, "name of an anchor node must contain at least one character", ); }
state.anchor = state.input.slice(position, state.position); return true;}
function readAlias(state: LoaderState): boolean { let ch = state.input.charCodeAt(state.position);
if (ch !== 0x2a /* * */) return false;
ch = state.input.charCodeAt(++state.position); const _position = state.position;
while (ch !== 0 && !isWsOrEol(ch) && !isFlowIndicator(ch)) { ch = state.input.charCodeAt(++state.position); }
if (state.position === _position) { return throwError( state, "name of an alias node must contain at least one character", ); }
const alias = state.input.slice(_position, state.position); if ( typeof state.anchorMap !== "undefined" && !hasOwn(state.anchorMap, alias) ) { return throwError(state, `unidentified alias "${alias}"`); }
if (typeof state.anchorMap !== "undefined") { state.result = state.anchorMap[alias]; } skipSeparationSpace(state, true, -1); return true;}
function composeNode( state: LoaderState, parentIndent: number, nodeContext: number, allowToSeek: boolean, allowCompact: boolean,): boolean { let allowBlockScalars: boolean, allowBlockCollections: boolean, indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this<parent atNewLine = false, hasContent = false, type: Type, flowIndent: number, blockIndent: number;
if (state.listener && state.listener !== null) { state.listener("open", state); }
state.tag = null; state.anchor = null; state.kind = null; state.result = null;
const allowBlockStyles = (allowBlockScalars = allowBlockCollections = CONTEXT_BLOCK_OUT === nodeContext || CONTEXT_BLOCK_IN === nodeContext);
if (allowToSeek) { if (skipSeparationSpace(state, true, -1)) { atNewLine = true;
if (state.lineIndent > parentIndent) { indentStatus = 1; } else if (state.lineIndent === parentIndent) { indentStatus = 0; } else if (state.lineIndent < parentIndent) { indentStatus = -1; } } }
if (indentStatus === 1) { while (readTagProperty(state) || readAnchorProperty(state)) { if (skipSeparationSpace(state, true, -1)) { atNewLine = true; allowBlockCollections = allowBlockStyles;
if (state.lineIndent > parentIndent) { indentStatus = 1; } else if (state.lineIndent === parentIndent) { indentStatus = 0; } else if (state.lineIndent < parentIndent) { indentStatus = -1; } } else { allowBlockCollections = false; } } }
if (allowBlockCollections) { allowBlockCollections = atNewLine || allowCompact; }
if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) { const cond = CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext; flowIndent = cond ? parentIndent : parentIndent + 1;
blockIndent = state.position - state.lineStart;
if (indentStatus === 1) { if ( (allowBlockCollections && (readBlockSequence(state, blockIndent) || readBlockMapping(state, blockIndent, flowIndent))) || readFlowCollection(state, flowIndent) ) { hasContent = true; } else { if ( (allowBlockScalars && readBlockScalar(state, flowIndent)) || readSingleQuotedScalar(state, flowIndent) || readDoubleQuotedScalar(state, flowIndent) ) { hasContent = true; } else if (readAlias(state)) { hasContent = true;
if (state.tag !== null || state.anchor !== null) { return throwError( state, "alias node should not have Any properties", ); } } else if ( readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext) ) { hasContent = true;
if (state.tag === null) { state.tag = "?"; } }
if (state.anchor !== null && typeof state.anchorMap !== "undefined") { state.anchorMap[state.anchor] = state.result; } } } else if (indentStatus === 0) { // Special case: block sequences are allowed to have same indentation level as the parent. // http://www.yaml.org/spec/1.2/spec.html#id2799784 hasContent = allowBlockCollections && readBlockSequence(state, blockIndent); } }
if (state.tag !== null && state.tag !== "!") { if (state.tag === "?") { for ( let typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex++ ) { type = state.implicitTypes[typeIndex];
// Implicit resolving is not allowed for non-scalar types, and '?' // non-specific tag is only assigned to plain scalars. So, it isn't // needed to check for 'kind' conformity.
if (type.resolve(state.result)) { // `state.result` updated in resolver if matched state.result = type.construct(state.result); state.tag = type.tag; if (state.anchor !== null && typeof state.anchorMap !== "undefined") { state.anchorMap[state.anchor] = state.result; } break; } } } else if ( hasOwn(state.typeMap[state.kind || "fallback"], state.tag) ) { type = state.typeMap[state.kind || "fallback"][state.tag];
if (state.result !== null && type.kind !== state.kind) { return throwError( state, `unacceptable node kind for !<${state.tag}> tag; it should be "${type.kind}", not "${state.kind}"`, ); }
if (!type.resolve(state.result)) { // `state.result` updated in resolver if matched return throwError( state, `cannot resolve a node with !<${state.tag}> explicit tag`, ); } else { state.result = type.construct(state.result); if (state.anchor !== null && typeof state.anchorMap !== "undefined") { state.anchorMap[state.anchor] = state.result; } } } else { return throwError(state, `unknown tag !<${state.tag}>`); } }
if (state.listener && state.listener !== null) { state.listener("close", state); } return state.tag !== null || state.anchor !== null || hasContent;}
function readDocument(state: LoaderState) { const documentStart = state.position; let position: number, directiveName: string, directiveArgs: string[], hasDirectives = false, ch: number;
state.version = null; state.checkLineBreaks = state.legacy; state.tagMap = Object.create(null); state.anchorMap = Object.create(null);
while ((ch = state.input.charCodeAt(state.position)) !== 0) { skipSeparationSpace(state, true, -1);
ch = state.input.charCodeAt(state.position);
if (state.lineIndent > 0 || ch !== 0x25 /* % */) { break; }
hasDirectives = true; ch = state.input.charCodeAt(++state.position); position = state.position;
while (ch !== 0 && !isWsOrEol(ch)) { ch = state.input.charCodeAt(++state.position); }
directiveName = state.input.slice(position, state.position); directiveArgs = [];
if (directiveName.length < 1) { return throwError( state, "directive name must not be less than one character in length", ); }
while (ch !== 0) { while (isWhiteSpace(ch)) { ch = state.input.charCodeAt(++state.position); }
if (ch === 0x23 /* # */) { do { ch = state.input.charCodeAt(++state.position); } while (ch !== 0 && !isEOL(ch)); break; }
if (isEOL(ch)) break;
position = state.position;
while (ch !== 0 && !isWsOrEol(ch)) { ch = state.input.charCodeAt(++state.position); }
directiveArgs.push(state.input.slice(position, state.position)); }
if (ch !== 0) readLineBreak(state);
if (hasOwn(directiveHandlers, directiveName)) { directiveHandlers[directiveName](state, directiveName, ...directiveArgs); } else { throwWarning(state, `unknown document directive "${directiveName}"`); } }
skipSeparationSpace(state, true, -1);
if ( state.lineIndent === 0 && state.input.charCodeAt(state.position) === 0x2d /* - */ && state.input.charCodeAt(state.position + 1) === 0x2d /* - */ && state.input.charCodeAt(state.position + 2) === 0x2d /* - */ ) { state.position += 3; skipSeparationSpace(state, true, -1); } else if (hasDirectives) { return throwError(state, "directives end mark is expected"); }
composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true); skipSeparationSpace(state, true, -1);
if ( state.checkLineBreaks && PATTERN_NON_ASCII_LINE_BREAKS.test( state.input.slice(documentStart, state.position), ) ) { throwWarning(state, "non-ASCII line breaks are interpreted as content"); }
state.documents.push(state.result);
if (state.position === state.lineStart && testDocumentSeparator(state)) { if (state.input.charCodeAt(state.position) === 0x2e /* . */) { state.position += 3; skipSeparationSpace(state, true, -1); } return; }
if (state.position < state.length - 1) { return throwError( state, "end of the stream or a document separator is expected", ); }}
function loadDocuments(input: string, options?: LoaderStateOptions): unknown[] { input = String(input); options = options || {};
if (input.length !== 0) { // Add tailing `\n` if not exists if ( input.charCodeAt(input.length - 1) !== 0x0a /* LF */ && input.charCodeAt(input.length - 1) !== 0x0d /* CR */ ) { input += "\n"; }
// Strip BOM if (input.charCodeAt(0) === 0xfeff) { input = input.slice(1); } }
const state = new LoaderState(input, options);
// Use 0 as string terminator. That significantly simplifies bounds check. state.input += "\0";
while (state.input.charCodeAt(state.position) === 0x20 /* Space */) { state.lineIndent += 1; state.position += 1; }
while (state.position < state.length - 1) { readDocument(state); }
return state.documents;}
export type CbFunction = (doc: unknown) => void;function isCbFunction(fn: unknown): fn is CbFunction { return typeof fn === "function";}
export function loadAll<T extends CbFunction | LoaderStateOptions>( input: string, iteratorOrOption?: T, options?: LoaderStateOptions,): T extends CbFunction ? void : unknown[] { if (!isCbFunction(iteratorOrOption)) { return loadDocuments(input, iteratorOrOption as LoaderStateOptions) as Any; }
const documents = loadDocuments(input, options); const iterator = iteratorOrOption; for (let index = 0, length = documents.length; index < length; index++) { iterator(documents[index]); }
return void 0 as Any;}
export function load(input: string, options?: LoaderStateOptions): unknown { const documents = loadDocuments(input, options);
if (documents.length === 0) { return; } if (documents.length === 1) { return documents[0]; } throw new YAMLError( "expected a single document in the stream, but found more", );}