Skip to main content
Module

std/encoding/_yaml/loader/loader.ts

Deno standard library
Go to Latest
File
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792
// 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-2022 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): 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 (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 = {}; } 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 (!hasOwn(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 && !hasOwn(overridableKeys, keyNode) && hasOwn(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; 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 = {}; 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): 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 (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", ); } 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", );}