Skip to main content
Module

std/encoding/_yaml/loader/loader.ts

Deno standard library
Go to Latest
File
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799
// 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-2021 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 _hasOwnProperty = Object.prototype.hasOwnProperty;
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 { /* eslint:disable:prettier */ 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" : ""; /* eslint:enable:prettier */}
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 = new Array(256); // integer, for fast accessconst simpleEscapeMap = new Array(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): void { 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[]): void { 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 (_hasOwnProperty.call(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 = {}; } state.tagMap[handle] = prefix; },};
function captureSegment( state: LoaderState, start: number, end: number, checkJson: boolean,): void { 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>,): void { 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 (!_hasOwnProperty.call(destination, key)) { destination[key] = (source as ArrayObject)[key]; 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 && !_hasOwnProperty.call(overridableKeys, keyNode) && _hasOwnProperty.call(result, keyNode) ) { state.line = startLine || state.line; state.position = startPos || state.position; return throwError(state, "duplicated mapping key"); } result[keyNode] = valueNode; delete overridableKeys[keyNode]; }
return result;}
function readLineBreak(state: LoaderState): void { 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): void { 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> = {}; 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; // eslint-disable-next-line @typescript-eslint/no-use-before-define 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); // eslint-disable-next-line @typescript-eslint/no-use-before-define 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; // eslint-disable-next-line @typescript-eslint/no-use-before-define 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 = {}; 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. // // eslint-disable-next-line @typescript-eslint/no-use-before-define } 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 ( // eslint-disable-next-line @typescript-eslint/no-use-before-define 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" && _hasOwnProperty.call(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" && !Object.prototype.hasOwnProperty.call(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 ( _hasOwnProperty.call(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): void { const documentStart = state.position; let position: number, directiveName: string, directiveArgs: string[], hasDirectives = false, ch: number;
state.version = null; state.checkLineBreaks = state.legacy; state.tagMap = {}; state.anchorMap = {};
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 (_hasOwnProperty.call(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", ); } else { return; }}
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", );}