resolveSeq-4a68b39b.js 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115
  1. 'use strict';
  2. var PlainValue = require('./PlainValue-ec8e588e.js');
  3. function addCommentBefore(str, indent, comment) {
  4. if (!comment) return str;
  5. const cc = comment.replace(/[\s\S]^/gm, `$&${indent}#`);
  6. return `#${cc}\n${indent}${str}`;
  7. }
  8. function addComment(str, indent, comment) {
  9. return !comment ? str : comment.indexOf('\n') === -1 ? `${str} #${comment}` : `${str}\n` + comment.replace(/^/gm, `${indent || ''}#`);
  10. }
  11. class Node {}
  12. function toJSON(value, arg, ctx) {
  13. if (Array.isArray(value)) return value.map((v, i) => toJSON(v, String(i), ctx));
  14. if (value && typeof value.toJSON === 'function') {
  15. const anchor = ctx && ctx.anchors && ctx.anchors.get(value);
  16. if (anchor) ctx.onCreate = res => {
  17. anchor.res = res;
  18. delete ctx.onCreate;
  19. };
  20. const res = value.toJSON(arg, ctx);
  21. if (anchor && ctx.onCreate) ctx.onCreate(res);
  22. return res;
  23. }
  24. if ((!ctx || !ctx.keep) && typeof value === 'bigint') return Number(value);
  25. return value;
  26. }
  27. class Scalar extends Node {
  28. constructor(value) {
  29. super();
  30. this.value = value;
  31. }
  32. toJSON(arg, ctx) {
  33. return ctx && ctx.keep ? this.value : toJSON(this.value, arg, ctx);
  34. }
  35. toString() {
  36. return String(this.value);
  37. }
  38. }
  39. function collectionFromPath(schema, path, value) {
  40. let v = value;
  41. for (let i = path.length - 1; i >= 0; --i) {
  42. const k = path[i];
  43. const o = Number.isInteger(k) && k >= 0 ? [] : {};
  44. o[k] = v;
  45. v = o;
  46. }
  47. return schema.createNode(v, false);
  48. } // null, undefined, or an empty non-string iterable (e.g. [])
  49. const isEmptyPath = path => path == null || typeof path === 'object' && path[Symbol.iterator]().next().done;
  50. class Collection extends Node {
  51. constructor(schema) {
  52. super();
  53. PlainValue._defineProperty(this, "items", []);
  54. this.schema = schema;
  55. }
  56. addIn(path, value) {
  57. if (isEmptyPath(path)) this.add(value);else {
  58. const [key, ...rest] = path;
  59. const node = this.get(key, true);
  60. if (node instanceof Collection) node.addIn(rest, value);else if (node === undefined && this.schema) this.set(key, collectionFromPath(this.schema, rest, value));else throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
  61. }
  62. }
  63. deleteIn([key, ...rest]) {
  64. if (rest.length === 0) return this.delete(key);
  65. const node = this.get(key, true);
  66. if (node instanceof Collection) return node.deleteIn(rest);else throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
  67. }
  68. getIn([key, ...rest], keepScalar) {
  69. const node = this.get(key, true);
  70. if (rest.length === 0) return !keepScalar && node instanceof Scalar ? node.value : node;else return node instanceof Collection ? node.getIn(rest, keepScalar) : undefined;
  71. }
  72. hasAllNullValues() {
  73. return this.items.every(node => {
  74. if (!node || node.type !== 'PAIR') return false;
  75. const n = node.value;
  76. return n == null || n instanceof Scalar && n.value == null && !n.commentBefore && !n.comment && !n.tag;
  77. });
  78. }
  79. hasIn([key, ...rest]) {
  80. if (rest.length === 0) return this.has(key);
  81. const node = this.get(key, true);
  82. return node instanceof Collection ? node.hasIn(rest) : false;
  83. }
  84. setIn([key, ...rest], value) {
  85. if (rest.length === 0) {
  86. this.set(key, value);
  87. } else {
  88. const node = this.get(key, true);
  89. if (node instanceof Collection) node.setIn(rest, value);else if (node === undefined && this.schema) this.set(key, collectionFromPath(this.schema, rest, value));else throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
  90. }
  91. } // overridden in implementations
  92. /* istanbul ignore next */
  93. toJSON() {
  94. return null;
  95. }
  96. toString(ctx, {
  97. blockItem,
  98. flowChars,
  99. isMap,
  100. itemIndent
  101. }, onComment, onChompKeep) {
  102. const {
  103. indent,
  104. indentStep,
  105. stringify
  106. } = ctx;
  107. const inFlow = this.type === PlainValue.Type.FLOW_MAP || this.type === PlainValue.Type.FLOW_SEQ || ctx.inFlow;
  108. if (inFlow) itemIndent += indentStep;
  109. const allNullValues = isMap && this.hasAllNullValues();
  110. ctx = Object.assign({}, ctx, {
  111. allNullValues,
  112. indent: itemIndent,
  113. inFlow,
  114. type: null
  115. });
  116. let chompKeep = false;
  117. let hasItemWithNewLine = false;
  118. const nodes = this.items.reduce((nodes, item, i) => {
  119. let comment;
  120. if (item) {
  121. if (!chompKeep && item.spaceBefore) nodes.push({
  122. type: 'comment',
  123. str: ''
  124. });
  125. if (item.commentBefore) item.commentBefore.match(/^.*$/gm).forEach(line => {
  126. nodes.push({
  127. type: 'comment',
  128. str: `#${line}`
  129. });
  130. });
  131. if (item.comment) comment = item.comment;
  132. if (inFlow && (!chompKeep && item.spaceBefore || item.commentBefore || item.comment || item.key && (item.key.commentBefore || item.key.comment) || item.value && (item.value.commentBefore || item.value.comment))) hasItemWithNewLine = true;
  133. }
  134. chompKeep = false;
  135. let str = stringify(item, ctx, () => comment = null, () => chompKeep = true);
  136. if (inFlow && !hasItemWithNewLine && str.includes('\n')) hasItemWithNewLine = true;
  137. if (inFlow && i < this.items.length - 1) str += ',';
  138. str = addComment(str, itemIndent, comment);
  139. if (chompKeep && (comment || inFlow)) chompKeep = false;
  140. nodes.push({
  141. type: 'item',
  142. str
  143. });
  144. return nodes;
  145. }, []);
  146. let str;
  147. if (nodes.length === 0) {
  148. str = flowChars.start + flowChars.end;
  149. } else if (inFlow) {
  150. const {
  151. start,
  152. end
  153. } = flowChars;
  154. const strings = nodes.map(n => n.str);
  155. if (hasItemWithNewLine || strings.reduce((sum, str) => sum + str.length + 2, 2) > Collection.maxFlowStringSingleLineLength) {
  156. str = start;
  157. for (const s of strings) {
  158. str += s ? `\n${indentStep}${indent}${s}` : '\n';
  159. }
  160. str += `\n${indent}${end}`;
  161. } else {
  162. str = `${start} ${strings.join(' ')} ${end}`;
  163. }
  164. } else {
  165. const strings = nodes.map(blockItem);
  166. str = strings.shift();
  167. for (const s of strings) str += s ? `\n${indent}${s}` : '\n';
  168. }
  169. if (this.comment) {
  170. str += '\n' + this.comment.replace(/^/gm, `${indent}#`);
  171. if (onComment) onComment();
  172. } else if (chompKeep && onChompKeep) onChompKeep();
  173. return str;
  174. }
  175. }
  176. PlainValue._defineProperty(Collection, "maxFlowStringSingleLineLength", 60);
  177. function asItemIndex(key) {
  178. let idx = key instanceof Scalar ? key.value : key;
  179. if (idx && typeof idx === 'string') idx = Number(idx);
  180. return Number.isInteger(idx) && idx >= 0 ? idx : null;
  181. }
  182. class YAMLSeq extends Collection {
  183. add(value) {
  184. this.items.push(value);
  185. }
  186. delete(key) {
  187. const idx = asItemIndex(key);
  188. if (typeof idx !== 'number') return false;
  189. const del = this.items.splice(idx, 1);
  190. return del.length > 0;
  191. }
  192. get(key, keepScalar) {
  193. const idx = asItemIndex(key);
  194. if (typeof idx !== 'number') return undefined;
  195. const it = this.items[idx];
  196. return !keepScalar && it instanceof Scalar ? it.value : it;
  197. }
  198. has(key) {
  199. const idx = asItemIndex(key);
  200. return typeof idx === 'number' && idx < this.items.length;
  201. }
  202. set(key, value) {
  203. const idx = asItemIndex(key);
  204. if (typeof idx !== 'number') throw new Error(`Expected a valid index, not ${key}.`);
  205. this.items[idx] = value;
  206. }
  207. toJSON(_, ctx) {
  208. const seq = [];
  209. if (ctx && ctx.onCreate) ctx.onCreate(seq);
  210. let i = 0;
  211. for (const item of this.items) seq.push(toJSON(item, String(i++), ctx));
  212. return seq;
  213. }
  214. toString(ctx, onComment, onChompKeep) {
  215. if (!ctx) return JSON.stringify(this);
  216. return super.toString(ctx, {
  217. blockItem: n => n.type === 'comment' ? n.str : `- ${n.str}`,
  218. flowChars: {
  219. start: '[',
  220. end: ']'
  221. },
  222. isMap: false,
  223. itemIndent: (ctx.indent || '') + ' '
  224. }, onComment, onChompKeep);
  225. }
  226. }
  227. const stringifyKey = (key, jsKey, ctx) => {
  228. if (jsKey === null) return '';
  229. if (typeof jsKey !== 'object') return String(jsKey);
  230. if (key instanceof Node && ctx && ctx.doc) return key.toString({
  231. anchors: {},
  232. doc: ctx.doc,
  233. indent: '',
  234. indentStep: ctx.indentStep,
  235. inFlow: true,
  236. inStringifyKey: true,
  237. stringify: ctx.stringify
  238. });
  239. return JSON.stringify(jsKey);
  240. };
  241. class Pair extends Node {
  242. constructor(key, value = null) {
  243. super();
  244. this.key = key;
  245. this.value = value;
  246. this.type = Pair.Type.PAIR;
  247. }
  248. get commentBefore() {
  249. return this.key instanceof Node ? this.key.commentBefore : undefined;
  250. }
  251. set commentBefore(cb) {
  252. if (this.key == null) this.key = new Scalar(null);
  253. if (this.key instanceof Node) this.key.commentBefore = cb;else {
  254. const msg = 'Pair.commentBefore is an alias for Pair.key.commentBefore. To set it, the key must be a Node.';
  255. throw new Error(msg);
  256. }
  257. }
  258. addToJSMap(ctx, map) {
  259. const key = toJSON(this.key, '', ctx);
  260. if (map instanceof Map) {
  261. const value = toJSON(this.value, key, ctx);
  262. map.set(key, value);
  263. } else if (map instanceof Set) {
  264. map.add(key);
  265. } else {
  266. const stringKey = stringifyKey(this.key, key, ctx);
  267. map[stringKey] = toJSON(this.value, stringKey, ctx);
  268. }
  269. return map;
  270. }
  271. toJSON(_, ctx) {
  272. const pair = ctx && ctx.mapAsMap ? new Map() : {};
  273. return this.addToJSMap(ctx, pair);
  274. }
  275. toString(ctx, onComment, onChompKeep) {
  276. if (!ctx || !ctx.doc) return JSON.stringify(this);
  277. const {
  278. indent: indentSize,
  279. indentSeq,
  280. simpleKeys
  281. } = ctx.doc.options;
  282. let {
  283. key,
  284. value
  285. } = this;
  286. let keyComment = key instanceof Node && key.comment;
  287. if (simpleKeys) {
  288. if (keyComment) {
  289. throw new Error('With simple keys, key nodes cannot have comments');
  290. }
  291. if (key instanceof Collection) {
  292. const msg = 'With simple keys, collection cannot be used as a key value';
  293. throw new Error(msg);
  294. }
  295. }
  296. const explicitKey = !simpleKeys && (!key || keyComment || key instanceof Collection || key.type === PlainValue.Type.BLOCK_FOLDED || key.type === PlainValue.Type.BLOCK_LITERAL);
  297. const {
  298. doc,
  299. indent,
  300. indentStep,
  301. stringify
  302. } = ctx;
  303. ctx = Object.assign({}, ctx, {
  304. implicitKey: !explicitKey,
  305. indent: indent + indentStep
  306. });
  307. let chompKeep = false;
  308. let str = stringify(key, ctx, () => keyComment = null, () => chompKeep = true);
  309. str = addComment(str, ctx.indent, keyComment);
  310. if (ctx.allNullValues && !simpleKeys) {
  311. if (this.comment) {
  312. str = addComment(str, ctx.indent, this.comment);
  313. if (onComment) onComment();
  314. } else if (chompKeep && !keyComment && onChompKeep) onChompKeep();
  315. return ctx.inFlow ? str : `? ${str}`;
  316. }
  317. str = explicitKey ? `? ${str}\n${indent}:` : `${str}:`;
  318. if (this.comment) {
  319. // expected (but not strictly required) to be a single-line comment
  320. str = addComment(str, ctx.indent, this.comment);
  321. if (onComment) onComment();
  322. }
  323. let vcb = '';
  324. let valueComment = null;
  325. if (value instanceof Node) {
  326. if (value.spaceBefore) vcb = '\n';
  327. if (value.commentBefore) {
  328. const cs = value.commentBefore.replace(/^/gm, `${ctx.indent}#`);
  329. vcb += `\n${cs}`;
  330. }
  331. valueComment = value.comment;
  332. } else if (value && typeof value === 'object') {
  333. value = doc.schema.createNode(value, true);
  334. }
  335. ctx.implicitKey = false;
  336. if (!explicitKey && !this.comment && value instanceof Scalar) ctx.indentAtStart = str.length + 1;
  337. chompKeep = false;
  338. if (!indentSeq && indentSize >= 2 && !ctx.inFlow && !explicitKey && value instanceof YAMLSeq && value.type !== PlainValue.Type.FLOW_SEQ && !value.tag && !doc.anchors.getName(value)) {
  339. // If indentSeq === false, consider '- ' as part of indentation where possible
  340. ctx.indent = ctx.indent.substr(2);
  341. }
  342. const valueStr = stringify(value, ctx, () => valueComment = null, () => chompKeep = true);
  343. let ws = ' ';
  344. if (vcb || this.comment) {
  345. ws = `${vcb}\n${ctx.indent}`;
  346. } else if (!explicitKey && value instanceof Collection) {
  347. const flow = valueStr[0] === '[' || valueStr[0] === '{';
  348. if (!flow || valueStr.includes('\n')) ws = `\n${ctx.indent}`;
  349. }
  350. if (chompKeep && !valueComment && onChompKeep) onChompKeep();
  351. return addComment(str + ws + valueStr, ctx.indent, valueComment);
  352. }
  353. }
  354. PlainValue._defineProperty(Pair, "Type", {
  355. PAIR: 'PAIR',
  356. MERGE_PAIR: 'MERGE_PAIR'
  357. });
  358. const getAliasCount = (node, anchors) => {
  359. if (node instanceof Alias) {
  360. const anchor = anchors.get(node.source);
  361. return anchor.count * anchor.aliasCount;
  362. } else if (node instanceof Collection) {
  363. let count = 0;
  364. for (const item of node.items) {
  365. const c = getAliasCount(item, anchors);
  366. if (c > count) count = c;
  367. }
  368. return count;
  369. } else if (node instanceof Pair) {
  370. const kc = getAliasCount(node.key, anchors);
  371. const vc = getAliasCount(node.value, anchors);
  372. return Math.max(kc, vc);
  373. }
  374. return 1;
  375. };
  376. class Alias extends Node {
  377. static stringify({
  378. range,
  379. source
  380. }, {
  381. anchors,
  382. doc,
  383. implicitKey,
  384. inStringifyKey
  385. }) {
  386. let anchor = Object.keys(anchors).find(a => anchors[a] === source);
  387. if (!anchor && inStringifyKey) anchor = doc.anchors.getName(source) || doc.anchors.newName();
  388. if (anchor) return `*${anchor}${implicitKey ? ' ' : ''}`;
  389. const msg = doc.anchors.getName(source) ? 'Alias node must be after source node' : 'Source node not found for alias node';
  390. throw new Error(`${msg} [${range}]`);
  391. }
  392. constructor(source) {
  393. super();
  394. this.source = source;
  395. this.type = PlainValue.Type.ALIAS;
  396. }
  397. set tag(t) {
  398. throw new Error('Alias nodes cannot have tags');
  399. }
  400. toJSON(arg, ctx) {
  401. if (!ctx) return toJSON(this.source, arg, ctx);
  402. const {
  403. anchors,
  404. maxAliasCount
  405. } = ctx;
  406. const anchor = anchors.get(this.source);
  407. /* istanbul ignore if */
  408. if (!anchor || anchor.res === undefined) {
  409. const msg = 'This should not happen: Alias anchor was not resolved?';
  410. if (this.cstNode) throw new PlainValue.YAMLReferenceError(this.cstNode, msg);else throw new ReferenceError(msg);
  411. }
  412. if (maxAliasCount >= 0) {
  413. anchor.count += 1;
  414. if (anchor.aliasCount === 0) anchor.aliasCount = getAliasCount(this.source, anchors);
  415. if (anchor.count * anchor.aliasCount > maxAliasCount) {
  416. const msg = 'Excessive alias count indicates a resource exhaustion attack';
  417. if (this.cstNode) throw new PlainValue.YAMLReferenceError(this.cstNode, msg);else throw new ReferenceError(msg);
  418. }
  419. }
  420. return anchor.res;
  421. } // Only called when stringifying an alias mapping key while constructing
  422. // Object output.
  423. toString(ctx) {
  424. return Alias.stringify(this, ctx);
  425. }
  426. }
  427. PlainValue._defineProperty(Alias, "default", true);
  428. function findPair(items, key) {
  429. const k = key instanceof Scalar ? key.value : key;
  430. for (const it of items) {
  431. if (it instanceof Pair) {
  432. if (it.key === key || it.key === k) return it;
  433. if (it.key && it.key.value === k) return it;
  434. }
  435. }
  436. return undefined;
  437. }
  438. class YAMLMap extends Collection {
  439. add(pair, overwrite) {
  440. if (!pair) pair = new Pair(pair);else if (!(pair instanceof Pair)) pair = new Pair(pair.key || pair, pair.value);
  441. const prev = findPair(this.items, pair.key);
  442. const sortEntries = this.schema && this.schema.sortMapEntries;
  443. if (prev) {
  444. if (overwrite) prev.value = pair.value;else throw new Error(`Key ${pair.key} already set`);
  445. } else if (sortEntries) {
  446. const i = this.items.findIndex(item => sortEntries(pair, item) < 0);
  447. if (i === -1) this.items.push(pair);else this.items.splice(i, 0, pair);
  448. } else {
  449. this.items.push(pair);
  450. }
  451. }
  452. delete(key) {
  453. const it = findPair(this.items, key);
  454. if (!it) return false;
  455. const del = this.items.splice(this.items.indexOf(it), 1);
  456. return del.length > 0;
  457. }
  458. get(key, keepScalar) {
  459. const it = findPair(this.items, key);
  460. const node = it && it.value;
  461. return !keepScalar && node instanceof Scalar ? node.value : node;
  462. }
  463. has(key) {
  464. return !!findPair(this.items, key);
  465. }
  466. set(key, value) {
  467. this.add(new Pair(key, value), true);
  468. }
  469. /**
  470. * @param {*} arg ignored
  471. * @param {*} ctx Conversion context, originally set in Document#toJSON()
  472. * @param {Class} Type If set, forces the returned collection type
  473. * @returns {*} Instance of Type, Map, or Object
  474. */
  475. toJSON(_, ctx, Type) {
  476. const map = Type ? new Type() : ctx && ctx.mapAsMap ? new Map() : {};
  477. if (ctx && ctx.onCreate) ctx.onCreate(map);
  478. for (const item of this.items) item.addToJSMap(ctx, map);
  479. return map;
  480. }
  481. toString(ctx, onComment, onChompKeep) {
  482. if (!ctx) return JSON.stringify(this);
  483. for (const item of this.items) {
  484. if (!(item instanceof Pair)) throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`);
  485. }
  486. return super.toString(ctx, {
  487. blockItem: n => n.str,
  488. flowChars: {
  489. start: '{',
  490. end: '}'
  491. },
  492. isMap: true,
  493. itemIndent: ctx.indent || ''
  494. }, onComment, onChompKeep);
  495. }
  496. }
  497. const MERGE_KEY = '<<';
  498. class Merge extends Pair {
  499. constructor(pair) {
  500. if (pair instanceof Pair) {
  501. let seq = pair.value;
  502. if (!(seq instanceof YAMLSeq)) {
  503. seq = new YAMLSeq();
  504. seq.items.push(pair.value);
  505. seq.range = pair.value.range;
  506. }
  507. super(pair.key, seq);
  508. this.range = pair.range;
  509. } else {
  510. super(new Scalar(MERGE_KEY), new YAMLSeq());
  511. }
  512. this.type = Pair.Type.MERGE_PAIR;
  513. } // If the value associated with a merge key is a single mapping node, each of
  514. // its key/value pairs is inserted into the current mapping, unless the key
  515. // already exists in it. If the value associated with the merge key is a
  516. // sequence, then this sequence is expected to contain mapping nodes and each
  517. // of these nodes is merged in turn according to its order in the sequence.
  518. // Keys in mapping nodes earlier in the sequence override keys specified in
  519. // later mapping nodes. -- http://yaml.org/type/merge.html
  520. addToJSMap(ctx, map) {
  521. for (const {
  522. source
  523. } of this.value.items) {
  524. if (!(source instanceof YAMLMap)) throw new Error('Merge sources must be maps');
  525. const srcMap = source.toJSON(null, ctx, Map);
  526. for (const [key, value] of srcMap) {
  527. if (map instanceof Map) {
  528. if (!map.has(key)) map.set(key, value);
  529. } else if (map instanceof Set) {
  530. map.add(key);
  531. } else {
  532. if (!Object.prototype.hasOwnProperty.call(map, key)) map[key] = value;
  533. }
  534. }
  535. }
  536. return map;
  537. }
  538. toString(ctx, onComment) {
  539. const seq = this.value;
  540. if (seq.items.length > 1) return super.toString(ctx, onComment);
  541. this.value = seq.items[0];
  542. const str = super.toString(ctx, onComment);
  543. this.value = seq;
  544. return str;
  545. }
  546. }
  547. const binaryOptions = {
  548. defaultType: PlainValue.Type.BLOCK_LITERAL,
  549. lineWidth: 76
  550. };
  551. const boolOptions = {
  552. trueStr: 'true',
  553. falseStr: 'false'
  554. };
  555. const intOptions = {
  556. asBigInt: false
  557. };
  558. const nullOptions = {
  559. nullStr: 'null'
  560. };
  561. const strOptions = {
  562. defaultType: PlainValue.Type.PLAIN,
  563. doubleQuoted: {
  564. jsonEncoding: false,
  565. minMultiLineLength: 40
  566. },
  567. fold: {
  568. lineWidth: 80,
  569. minContentWidth: 20
  570. }
  571. };
  572. function resolveScalar(str, tags, scalarFallback) {
  573. for (const {
  574. format,
  575. test,
  576. resolve
  577. } of tags) {
  578. if (test) {
  579. const match = str.match(test);
  580. if (match) {
  581. let res = resolve.apply(null, match);
  582. if (!(res instanceof Scalar)) res = new Scalar(res);
  583. if (format) res.format = format;
  584. return res;
  585. }
  586. }
  587. }
  588. if (scalarFallback) str = scalarFallback(str);
  589. return new Scalar(str);
  590. }
  591. const FOLD_FLOW = 'flow';
  592. const FOLD_BLOCK = 'block';
  593. const FOLD_QUOTED = 'quoted'; // presumes i+1 is at the start of a line
  594. // returns index of last newline in more-indented block
  595. const consumeMoreIndentedLines = (text, i) => {
  596. let ch = text[i + 1];
  597. while (ch === ' ' || ch === '\t') {
  598. do {
  599. ch = text[i += 1];
  600. } while (ch && ch !== '\n');
  601. ch = text[i + 1];
  602. }
  603. return i;
  604. };
  605. /**
  606. * Tries to keep input at up to `lineWidth` characters, splitting only on spaces
  607. * not followed by newlines or spaces unless `mode` is `'quoted'`. Lines are
  608. * terminated with `\n` and started with `indent`.
  609. *
  610. * @param {string} text
  611. * @param {string} indent
  612. * @param {string} [mode='flow'] `'block'` prevents more-indented lines
  613. * from being folded; `'quoted'` allows for `\` escapes, including escaped
  614. * newlines
  615. * @param {Object} options
  616. * @param {number} [options.indentAtStart] Accounts for leading contents on
  617. * the first line, defaulting to `indent.length`
  618. * @param {number} [options.lineWidth=80]
  619. * @param {number} [options.minContentWidth=20] Allow highly indented lines to
  620. * stretch the line width
  621. * @param {function} options.onFold Called once if the text is folded
  622. * @param {function} options.onFold Called once if any line of text exceeds
  623. * lineWidth characters
  624. */
  625. function foldFlowLines(text, indent, mode, {
  626. indentAtStart,
  627. lineWidth = 80,
  628. minContentWidth = 20,
  629. onFold,
  630. onOverflow
  631. }) {
  632. if (!lineWidth || lineWidth < 0) return text;
  633. const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length);
  634. if (text.length <= endStep) return text;
  635. const folds = [];
  636. const escapedFolds = {};
  637. let end = lineWidth - (typeof indentAtStart === 'number' ? indentAtStart : indent.length);
  638. let split = undefined;
  639. let prev = undefined;
  640. let overflow = false;
  641. let i = -1;
  642. if (mode === FOLD_BLOCK) {
  643. i = consumeMoreIndentedLines(text, i);
  644. if (i !== -1) end = i + endStep;
  645. }
  646. for (let ch; ch = text[i += 1];) {
  647. if (mode === FOLD_QUOTED && ch === '\\') {
  648. switch (text[i + 1]) {
  649. case 'x':
  650. i += 3;
  651. break;
  652. case 'u':
  653. i += 5;
  654. break;
  655. case 'U':
  656. i += 9;
  657. break;
  658. default:
  659. i += 1;
  660. }
  661. }
  662. if (ch === '\n') {
  663. if (mode === FOLD_BLOCK) i = consumeMoreIndentedLines(text, i);
  664. end = i + endStep;
  665. split = undefined;
  666. } else {
  667. if (ch === ' ' && prev && prev !== ' ' && prev !== '\n' && prev !== '\t') {
  668. // space surrounded by non-space can be replaced with newline + indent
  669. const next = text[i + 1];
  670. if (next && next !== ' ' && next !== '\n' && next !== '\t') split = i;
  671. }
  672. if (i >= end) {
  673. if (split) {
  674. folds.push(split);
  675. end = split + endStep;
  676. split = undefined;
  677. } else if (mode === FOLD_QUOTED) {
  678. // white-space collected at end may stretch past lineWidth
  679. while (prev === ' ' || prev === '\t') {
  680. prev = ch;
  681. ch = text[i += 1];
  682. overflow = true;
  683. } // i - 2 accounts for not-dropped last char + newline-escaping \
  684. folds.push(i - 2);
  685. escapedFolds[i - 2] = true;
  686. end = i - 2 + endStep;
  687. split = undefined;
  688. } else {
  689. overflow = true;
  690. }
  691. }
  692. }
  693. prev = ch;
  694. }
  695. if (overflow && onOverflow) onOverflow();
  696. if (folds.length === 0) return text;
  697. if (onFold) onFold();
  698. let res = text.slice(0, folds[0]);
  699. for (let i = 0; i < folds.length; ++i) {
  700. const fold = folds[i];
  701. const end = folds[i + 1] || text.length;
  702. if (mode === FOLD_QUOTED && escapedFolds[fold]) res += `${text[fold]}\\`;
  703. res += `\n${indent}${text.slice(fold + 1, end)}`;
  704. }
  705. return res;
  706. }
  707. const getFoldOptions = ({
  708. indentAtStart
  709. }) => indentAtStart ? Object.assign({
  710. indentAtStart
  711. }, strOptions.fold) : strOptions.fold; // Also checks for lines starting with %, as parsing the output as YAML 1.1 will
  712. // presume that's starting a new document.
  713. const containsDocumentMarker = str => /^(%|---|\.\.\.)/m.test(str);
  714. function lineLengthOverLimit(str, limit) {
  715. const strLen = str.length;
  716. if (strLen <= limit) return false;
  717. for (let i = 0, start = 0; i < strLen; ++i) {
  718. if (str[i] === '\n') {
  719. if (i - start > limit) return true;
  720. start = i + 1;
  721. if (strLen - start <= limit) return false;
  722. }
  723. }
  724. return true;
  725. }
  726. function doubleQuotedString(value, ctx) {
  727. const {
  728. implicitKey
  729. } = ctx;
  730. const {
  731. jsonEncoding,
  732. minMultiLineLength
  733. } = strOptions.doubleQuoted;
  734. const json = JSON.stringify(value);
  735. if (jsonEncoding) return json;
  736. const indent = ctx.indent || (containsDocumentMarker(value) ? ' ' : '');
  737. let str = '';
  738. let start = 0;
  739. for (let i = 0, ch = json[i]; ch; ch = json[++i]) {
  740. if (ch === ' ' && json[i + 1] === '\\' && json[i + 2] === 'n') {
  741. // space before newline needs to be escaped to not be folded
  742. str += json.slice(start, i) + '\\ ';
  743. i += 1;
  744. start = i;
  745. ch = '\\';
  746. }
  747. if (ch === '\\') switch (json[i + 1]) {
  748. case 'u':
  749. {
  750. str += json.slice(start, i);
  751. const code = json.substr(i + 2, 4);
  752. switch (code) {
  753. case '0000':
  754. str += '\\0';
  755. break;
  756. case '0007':
  757. str += '\\a';
  758. break;
  759. case '000b':
  760. str += '\\v';
  761. break;
  762. case '001b':
  763. str += '\\e';
  764. break;
  765. case '0085':
  766. str += '\\N';
  767. break;
  768. case '00a0':
  769. str += '\\_';
  770. break;
  771. case '2028':
  772. str += '\\L';
  773. break;
  774. case '2029':
  775. str += '\\P';
  776. break;
  777. default:
  778. if (code.substr(0, 2) === '00') str += '\\x' + code.substr(2);else str += json.substr(i, 6);
  779. }
  780. i += 5;
  781. start = i + 1;
  782. }
  783. break;
  784. case 'n':
  785. if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) {
  786. i += 1;
  787. } else {
  788. // folding will eat first newline
  789. str += json.slice(start, i) + '\n\n';
  790. while (json[i + 2] === '\\' && json[i + 3] === 'n' && json[i + 4] !== '"') {
  791. str += '\n';
  792. i += 2;
  793. }
  794. str += indent; // space after newline needs to be escaped to not be folded
  795. if (json[i + 2] === ' ') str += '\\';
  796. i += 1;
  797. start = i + 1;
  798. }
  799. break;
  800. default:
  801. i += 1;
  802. }
  803. }
  804. str = start ? str + json.slice(start) : json;
  805. return implicitKey ? str : foldFlowLines(str, indent, FOLD_QUOTED, getFoldOptions(ctx));
  806. }
  807. function singleQuotedString(value, ctx) {
  808. if (ctx.implicitKey) {
  809. if (/\n/.test(value)) return doubleQuotedString(value, ctx);
  810. } else {
  811. // single quoted string can't have leading or trailing whitespace around newline
  812. if (/[ \t]\n|\n[ \t]/.test(value)) return doubleQuotedString(value, ctx);
  813. }
  814. const indent = ctx.indent || (containsDocumentMarker(value) ? ' ' : '');
  815. const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$&\n${indent}`) + "'";
  816. return ctx.implicitKey ? res : foldFlowLines(res, indent, FOLD_FLOW, getFoldOptions(ctx));
  817. }
  818. function blockString({
  819. comment,
  820. type,
  821. value
  822. }, ctx, onComment, onChompKeep) {
  823. // 1. Block can't end in whitespace unless the last line is non-empty.
  824. // 2. Strings consisting of only whitespace are best rendered explicitly.
  825. if (/\n[\t ]+$/.test(value) || /^\s*$/.test(value)) {
  826. return doubleQuotedString(value, ctx);
  827. }
  828. const indent = ctx.indent || (ctx.forceBlockIndent || containsDocumentMarker(value) ? ' ' : '');
  829. const indentSize = indent ? '2' : '1'; // root is at -1
  830. const literal = type === PlainValue.Type.BLOCK_FOLDED ? false : type === PlainValue.Type.BLOCK_LITERAL ? true : !lineLengthOverLimit(value, strOptions.fold.lineWidth - indent.length);
  831. let header = literal ? '|' : '>';
  832. if (!value) return header + '\n';
  833. let wsStart = '';
  834. let wsEnd = '';
  835. value = value.replace(/[\n\t ]*$/, ws => {
  836. const n = ws.indexOf('\n');
  837. if (n === -1) {
  838. header += '-'; // strip
  839. } else if (value === ws || n !== ws.length - 1) {
  840. header += '+'; // keep
  841. if (onChompKeep) onChompKeep();
  842. }
  843. wsEnd = ws.replace(/\n$/, '');
  844. return '';
  845. }).replace(/^[\n ]*/, ws => {
  846. if (ws.indexOf(' ') !== -1) header += indentSize;
  847. const m = ws.match(/ +$/);
  848. if (m) {
  849. wsStart = ws.slice(0, -m[0].length);
  850. return m[0];
  851. } else {
  852. wsStart = ws;
  853. return '';
  854. }
  855. });
  856. if (wsEnd) wsEnd = wsEnd.replace(/\n+(?!\n|$)/g, `$&${indent}`);
  857. if (wsStart) wsStart = wsStart.replace(/\n+/g, `$&${indent}`);
  858. if (comment) {
  859. header += ' #' + comment.replace(/ ?[\r\n]+/g, ' ');
  860. if (onComment) onComment();
  861. }
  862. if (!value) return `${header}${indentSize}\n${indent}${wsEnd}`;
  863. if (literal) {
  864. value = value.replace(/\n+/g, `$&${indent}`);
  865. return `${header}\n${indent}${wsStart}${value}${wsEnd}`;
  866. }
  867. value = value.replace(/\n+/g, '\n$&').replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, '$1$2') // more-indented lines aren't folded
  868. // ^ ind.line ^ empty ^ capture next empty lines only at end of indent
  869. .replace(/\n+/g, `$&${indent}`);
  870. const body = foldFlowLines(`${wsStart}${value}${wsEnd}`, indent, FOLD_BLOCK, strOptions.fold);
  871. return `${header}\n${indent}${body}`;
  872. }
  873. function plainString(item, ctx, onComment, onChompKeep) {
  874. const {
  875. comment,
  876. type,
  877. value
  878. } = item;
  879. const {
  880. actualString,
  881. implicitKey,
  882. indent,
  883. inFlow
  884. } = ctx;
  885. if (implicitKey && /[\n[\]{},]/.test(value) || inFlow && /[[\]{},]/.test(value)) {
  886. return doubleQuotedString(value, ctx);
  887. }
  888. if (!value || /^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) {
  889. // not allowed:
  890. // - empty string, '-' or '?'
  891. // - start with an indicator character (except [?:-]) or /[?-] /
  892. // - '\n ', ': ' or ' \n' anywhere
  893. // - '#' not preceded by a non-space char
  894. // - end with ' ' or ':'
  895. return implicitKey || inFlow || value.indexOf('\n') === -1 ? value.indexOf('"') !== -1 && value.indexOf("'") === -1 ? singleQuotedString(value, ctx) : doubleQuotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep);
  896. }
  897. if (!implicitKey && !inFlow && type !== PlainValue.Type.PLAIN && value.indexOf('\n') !== -1) {
  898. // Where allowed & type not set explicitly, prefer block style for multiline strings
  899. return blockString(item, ctx, onComment, onChompKeep);
  900. }
  901. if (indent === '' && containsDocumentMarker(value)) {
  902. ctx.forceBlockIndent = true;
  903. return blockString(item, ctx, onComment, onChompKeep);
  904. }
  905. const str = value.replace(/\n+/g, `$&\n${indent}`); // Verify that output will be parsed as a string, as e.g. plain numbers and
  906. // booleans get parsed with those types in v1.2 (e.g. '42', 'true' & '0.9e-3'),
  907. // and others in v1.1.
  908. if (actualString) {
  909. const {
  910. tags
  911. } = ctx.doc.schema;
  912. const resolved = resolveScalar(str, tags, tags.scalarFallback).value;
  913. if (typeof resolved !== 'string') return doubleQuotedString(value, ctx);
  914. }
  915. const body = implicitKey ? str : foldFlowLines(str, indent, FOLD_FLOW, getFoldOptions(ctx));
  916. if (comment && !inFlow && (body.indexOf('\n') !== -1 || comment.indexOf('\n') !== -1)) {
  917. if (onComment) onComment();
  918. return addCommentBefore(body, indent, comment);
  919. }
  920. return body;
  921. }
  922. function stringifyString(item, ctx, onComment, onChompKeep) {
  923. const {
  924. defaultType
  925. } = strOptions;
  926. const {
  927. implicitKey,
  928. inFlow
  929. } = ctx;
  930. let {
  931. type,
  932. value
  933. } = item;
  934. if (typeof value !== 'string') {
  935. value = String(value);
  936. item = Object.assign({}, item, {
  937. value
  938. });
  939. }
  940. const _stringify = _type => {
  941. switch (_type) {
  942. case PlainValue.Type.BLOCK_FOLDED:
  943. case PlainValue.Type.BLOCK_LITERAL:
  944. return blockString(item, ctx, onComment, onChompKeep);
  945. case PlainValue.Type.QUOTE_DOUBLE:
  946. return doubleQuotedString(value, ctx);
  947. case PlainValue.Type.QUOTE_SINGLE:
  948. return singleQuotedString(value, ctx);
  949. case PlainValue.Type.PLAIN:
  950. return plainString(item, ctx, onComment, onChompKeep);
  951. default:
  952. return null;
  953. }
  954. };
  955. if (type !== PlainValue.Type.QUOTE_DOUBLE && /[\x00-\x08\x0b-\x1f\x7f-\x9f]/.test(value)) {
  956. // force double quotes on control characters
  957. type = PlainValue.Type.QUOTE_DOUBLE;
  958. } else if ((implicitKey || inFlow) && (type === PlainValue.Type.BLOCK_FOLDED || type === PlainValue.Type.BLOCK_LITERAL)) {
  959. // should not happen; blocks are not valid inside flow containers
  960. type = PlainValue.Type.QUOTE_DOUBLE;
  961. }
  962. let res = _stringify(type);
  963. if (res === null) {
  964. res = _stringify(defaultType);
  965. if (res === null) throw new Error(`Unsupported default string type ${defaultType}`);
  966. }
  967. return res;
  968. }
  969. function stringifyNumber({
  970. format,
  971. minFractionDigits,
  972. tag,
  973. value
  974. }) {
  975. if (typeof value === 'bigint') return String(value);
  976. if (!isFinite(value)) return isNaN(value) ? '.nan' : value < 0 ? '-.inf' : '.inf';
  977. let n = JSON.stringify(value);
  978. if (!format && minFractionDigits && (!tag || tag === 'tag:yaml.org,2002:float') && /^\d/.test(n)) {
  979. let i = n.indexOf('.');
  980. if (i < 0) {
  981. i = n.length;
  982. n += '.';
  983. }
  984. let d = minFractionDigits - (n.length - i - 1);
  985. while (d-- > 0) n += '0';
  986. }
  987. return n;
  988. }
  989. function checkFlowCollectionEnd(errors, cst) {
  990. let char, name;
  991. switch (cst.type) {
  992. case PlainValue.Type.FLOW_MAP:
  993. char = '}';
  994. name = 'flow map';
  995. break;
  996. case PlainValue.Type.FLOW_SEQ:
  997. char = ']';
  998. name = 'flow sequence';
  999. break;
  1000. default:
  1001. errors.push(new PlainValue.YAMLSemanticError(cst, 'Not a flow collection!?'));
  1002. return;
  1003. }
  1004. let lastItem;
  1005. for (let i = cst.items.length - 1; i >= 0; --i) {
  1006. const item = cst.items[i];
  1007. if (!item || item.type !== PlainValue.Type.COMMENT) {
  1008. lastItem = item;
  1009. break;
  1010. }
  1011. }
  1012. if (lastItem && lastItem.char !== char) {
  1013. const msg = `Expected ${name} to end with ${char}`;
  1014. let err;
  1015. if (typeof lastItem.offset === 'number') {
  1016. err = new PlainValue.YAMLSemanticError(cst, msg);
  1017. err.offset = lastItem.offset + 1;
  1018. } else {
  1019. err = new PlainValue.YAMLSemanticError(lastItem, msg);
  1020. if (lastItem.range && lastItem.range.end) err.offset = lastItem.range.end - lastItem.range.start;
  1021. }
  1022. errors.push(err);
  1023. }
  1024. }
  1025. function checkFlowCommentSpace(errors, comment) {
  1026. const prev = comment.context.src[comment.range.start - 1];
  1027. if (prev !== '\n' && prev !== '\t' && prev !== ' ') {
  1028. const msg = 'Comments must be separated from other tokens by white space characters';
  1029. errors.push(new PlainValue.YAMLSemanticError(comment, msg));
  1030. }
  1031. }
  1032. function getLongKeyError(source, key) {
  1033. const sk = String(key);
  1034. const k = sk.substr(0, 8) + '...' + sk.substr(-8);
  1035. return new PlainValue.YAMLSemanticError(source, `The "${k}" key is too long`);
  1036. }
  1037. function resolveComments(collection, comments) {
  1038. for (const {
  1039. afterKey,
  1040. before,
  1041. comment
  1042. } of comments) {
  1043. let item = collection.items[before];
  1044. if (!item) {
  1045. if (comment !== undefined) {
  1046. if (collection.comment) collection.comment += '\n' + comment;else collection.comment = comment;
  1047. }
  1048. } else {
  1049. if (afterKey && item.value) item = item.value;
  1050. if (comment === undefined) {
  1051. if (afterKey || !item.commentBefore) item.spaceBefore = true;
  1052. } else {
  1053. if (item.commentBefore) item.commentBefore += '\n' + comment;else item.commentBefore = comment;
  1054. }
  1055. }
  1056. }
  1057. }
  1058. // on error, will return { str: string, errors: Error[] }
  1059. function resolveString(doc, node) {
  1060. const res = node.strValue;
  1061. if (!res) return '';
  1062. if (typeof res === 'string') return res;
  1063. res.errors.forEach(error => {
  1064. if (!error.source) error.source = node;
  1065. doc.errors.push(error);
  1066. });
  1067. return res.str;
  1068. }
  1069. function resolveTagHandle(doc, node) {
  1070. const {
  1071. handle,
  1072. suffix
  1073. } = node.tag;
  1074. let prefix = doc.tagPrefixes.find(p => p.handle === handle);
  1075. if (!prefix) {
  1076. const dtp = doc.getDefaults().tagPrefixes;
  1077. if (dtp) prefix = dtp.find(p => p.handle === handle);
  1078. if (!prefix) throw new PlainValue.YAMLSemanticError(node, `The ${handle} tag handle is non-default and was not declared.`);
  1079. }
  1080. if (!suffix) throw new PlainValue.YAMLSemanticError(node, `The ${handle} tag has no suffix.`);
  1081. if (handle === '!' && (doc.version || doc.options.version) === '1.0') {
  1082. if (suffix[0] === '^') {
  1083. doc.warnings.push(new PlainValue.YAMLWarning(node, 'YAML 1.0 ^ tag expansion is not supported'));
  1084. return suffix;
  1085. }
  1086. if (/[:/]/.test(suffix)) {
  1087. // word/foo -> tag:word.yaml.org,2002:foo
  1088. const vocab = suffix.match(/^([a-z0-9-]+)\/(.*)/i);
  1089. return vocab ? `tag:${vocab[1]}.yaml.org,2002:${vocab[2]}` : `tag:${suffix}`;
  1090. }
  1091. }
  1092. return prefix.prefix + decodeURIComponent(suffix);
  1093. }
  1094. function resolveTagName(doc, node) {
  1095. const {
  1096. tag,
  1097. type
  1098. } = node;
  1099. let nonSpecific = false;
  1100. if (tag) {
  1101. const {
  1102. handle,
  1103. suffix,
  1104. verbatim
  1105. } = tag;
  1106. if (verbatim) {
  1107. if (verbatim !== '!' && verbatim !== '!!') return verbatim;
  1108. const msg = `Verbatim tags aren't resolved, so ${verbatim} is invalid.`;
  1109. doc.errors.push(new PlainValue.YAMLSemanticError(node, msg));
  1110. } else if (handle === '!' && !suffix) {
  1111. nonSpecific = true;
  1112. } else {
  1113. try {
  1114. return resolveTagHandle(doc, node);
  1115. } catch (error) {
  1116. doc.errors.push(error);
  1117. }
  1118. }
  1119. }
  1120. switch (type) {
  1121. case PlainValue.Type.BLOCK_FOLDED:
  1122. case PlainValue.Type.BLOCK_LITERAL:
  1123. case PlainValue.Type.QUOTE_DOUBLE:
  1124. case PlainValue.Type.QUOTE_SINGLE:
  1125. return PlainValue.defaultTags.STR;
  1126. case PlainValue.Type.FLOW_MAP:
  1127. case PlainValue.Type.MAP:
  1128. return PlainValue.defaultTags.MAP;
  1129. case PlainValue.Type.FLOW_SEQ:
  1130. case PlainValue.Type.SEQ:
  1131. return PlainValue.defaultTags.SEQ;
  1132. case PlainValue.Type.PLAIN:
  1133. return nonSpecific ? PlainValue.defaultTags.STR : null;
  1134. default:
  1135. return null;
  1136. }
  1137. }
  1138. function resolveByTagName(doc, node, tagName) {
  1139. const {
  1140. tags
  1141. } = doc.schema;
  1142. const matchWithTest = [];
  1143. for (const tag of tags) {
  1144. if (tag.tag === tagName) {
  1145. if (tag.test) matchWithTest.push(tag);else {
  1146. const res = tag.resolve(doc, node);
  1147. return res instanceof Collection ? res : new Scalar(res);
  1148. }
  1149. }
  1150. }
  1151. const str = resolveString(doc, node);
  1152. if (typeof str === 'string' && matchWithTest.length > 0) return resolveScalar(str, matchWithTest, tags.scalarFallback);
  1153. return null;
  1154. }
  1155. function getFallbackTagName({
  1156. type
  1157. }) {
  1158. switch (type) {
  1159. case PlainValue.Type.FLOW_MAP:
  1160. case PlainValue.Type.MAP:
  1161. return PlainValue.defaultTags.MAP;
  1162. case PlainValue.Type.FLOW_SEQ:
  1163. case PlainValue.Type.SEQ:
  1164. return PlainValue.defaultTags.SEQ;
  1165. default:
  1166. return PlainValue.defaultTags.STR;
  1167. }
  1168. }
  1169. function resolveTag(doc, node, tagName) {
  1170. try {
  1171. const res = resolveByTagName(doc, node, tagName);
  1172. if (res) {
  1173. if (tagName && node.tag) res.tag = tagName;
  1174. return res;
  1175. }
  1176. } catch (error) {
  1177. /* istanbul ignore if */
  1178. if (!error.source) error.source = node;
  1179. doc.errors.push(error);
  1180. return null;
  1181. }
  1182. try {
  1183. const fallback = getFallbackTagName(node);
  1184. if (!fallback) throw new Error(`The tag ${tagName} is unavailable`);
  1185. const msg = `The tag ${tagName} is unavailable, falling back to ${fallback}`;
  1186. doc.warnings.push(new PlainValue.YAMLWarning(node, msg));
  1187. const res = resolveByTagName(doc, node, fallback);
  1188. res.tag = tagName;
  1189. return res;
  1190. } catch (error) {
  1191. const refError = new PlainValue.YAMLReferenceError(node, error.message);
  1192. refError.stack = error.stack;
  1193. doc.errors.push(refError);
  1194. return null;
  1195. }
  1196. }
  1197. const isCollectionItem = node => {
  1198. if (!node) return false;
  1199. const {
  1200. type
  1201. } = node;
  1202. return type === PlainValue.Type.MAP_KEY || type === PlainValue.Type.MAP_VALUE || type === PlainValue.Type.SEQ_ITEM;
  1203. };
  1204. function resolveNodeProps(errors, node) {
  1205. const comments = {
  1206. before: [],
  1207. after: []
  1208. };
  1209. let hasAnchor = false;
  1210. let hasTag = false;
  1211. const props = isCollectionItem(node.context.parent) ? node.context.parent.props.concat(node.props) : node.props;
  1212. for (const {
  1213. start,
  1214. end
  1215. } of props) {
  1216. switch (node.context.src[start]) {
  1217. case PlainValue.Char.COMMENT:
  1218. {
  1219. if (!node.commentHasRequiredWhitespace(start)) {
  1220. const msg = 'Comments must be separated from other tokens by white space characters';
  1221. errors.push(new PlainValue.YAMLSemanticError(node, msg));
  1222. }
  1223. const {
  1224. header,
  1225. valueRange
  1226. } = node;
  1227. const cc = valueRange && (start > valueRange.start || header && start > header.start) ? comments.after : comments.before;
  1228. cc.push(node.context.src.slice(start + 1, end));
  1229. break;
  1230. }
  1231. // Actual anchor & tag resolution is handled by schema, here we just complain
  1232. case PlainValue.Char.ANCHOR:
  1233. if (hasAnchor) {
  1234. const msg = 'A node can have at most one anchor';
  1235. errors.push(new PlainValue.YAMLSemanticError(node, msg));
  1236. }
  1237. hasAnchor = true;
  1238. break;
  1239. case PlainValue.Char.TAG:
  1240. if (hasTag) {
  1241. const msg = 'A node can have at most one tag';
  1242. errors.push(new PlainValue.YAMLSemanticError(node, msg));
  1243. }
  1244. hasTag = true;
  1245. break;
  1246. }
  1247. }
  1248. return {
  1249. comments,
  1250. hasAnchor,
  1251. hasTag
  1252. };
  1253. }
  1254. function resolveNodeValue(doc, node) {
  1255. const {
  1256. anchors,
  1257. errors,
  1258. schema
  1259. } = doc;
  1260. if (node.type === PlainValue.Type.ALIAS) {
  1261. const name = node.rawValue;
  1262. const src = anchors.getNode(name);
  1263. if (!src) {
  1264. const msg = `Aliased anchor not found: ${name}`;
  1265. errors.push(new PlainValue.YAMLReferenceError(node, msg));
  1266. return null;
  1267. } // Lazy resolution for circular references
  1268. const res = new Alias(src);
  1269. anchors._cstAliases.push(res);
  1270. return res;
  1271. }
  1272. const tagName = resolveTagName(doc, node);
  1273. if (tagName) return resolveTag(doc, node, tagName);
  1274. if (node.type !== PlainValue.Type.PLAIN) {
  1275. const msg = `Failed to resolve ${node.type} node here`;
  1276. errors.push(new PlainValue.YAMLSyntaxError(node, msg));
  1277. return null;
  1278. }
  1279. try {
  1280. const str = resolveString(doc, node);
  1281. return resolveScalar(str, schema.tags, schema.tags.scalarFallback);
  1282. } catch (error) {
  1283. if (!error.source) error.source = node;
  1284. errors.push(error);
  1285. return null;
  1286. }
  1287. } // sets node.resolved on success
  1288. function resolveNode(doc, node) {
  1289. if (!node) return null;
  1290. if (node.error) doc.errors.push(node.error);
  1291. const {
  1292. comments,
  1293. hasAnchor,
  1294. hasTag
  1295. } = resolveNodeProps(doc.errors, node);
  1296. if (hasAnchor) {
  1297. const {
  1298. anchors
  1299. } = doc;
  1300. const name = node.anchor;
  1301. const prev = anchors.getNode(name); // At this point, aliases for any preceding node with the same anchor
  1302. // name have already been resolved, so it may safely be renamed.
  1303. if (prev) anchors.map[anchors.newName(name)] = prev; // During parsing, we need to store the CST node in anchors.map as
  1304. // anchors need to be available during resolution to allow for
  1305. // circular references.
  1306. anchors.map[name] = node;
  1307. }
  1308. if (node.type === PlainValue.Type.ALIAS && (hasAnchor || hasTag)) {
  1309. const msg = 'An alias node must not specify any properties';
  1310. doc.errors.push(new PlainValue.YAMLSemanticError(node, msg));
  1311. }
  1312. const res = resolveNodeValue(doc, node);
  1313. if (res) {
  1314. res.range = [node.range.start, node.range.end];
  1315. if (doc.options.keepCstNodes) res.cstNode = node;
  1316. if (doc.options.keepNodeTypes) res.type = node.type;
  1317. const cb = comments.before.join('\n');
  1318. if (cb) {
  1319. res.commentBefore = res.commentBefore ? `${res.commentBefore}\n${cb}` : cb;
  1320. }
  1321. const ca = comments.after.join('\n');
  1322. if (ca) res.comment = res.comment ? `${res.comment}\n${ca}` : ca;
  1323. }
  1324. return node.resolved = res;
  1325. }
  1326. function resolveMap(doc, cst) {
  1327. if (cst.type !== PlainValue.Type.MAP && cst.type !== PlainValue.Type.FLOW_MAP) {
  1328. const msg = `A ${cst.type} node cannot be resolved as a mapping`;
  1329. doc.errors.push(new PlainValue.YAMLSyntaxError(cst, msg));
  1330. return null;
  1331. }
  1332. const {
  1333. comments,
  1334. items
  1335. } = cst.type === PlainValue.Type.FLOW_MAP ? resolveFlowMapItems(doc, cst) : resolveBlockMapItems(doc, cst);
  1336. const map = new YAMLMap();
  1337. map.items = items;
  1338. resolveComments(map, comments);
  1339. let hasCollectionKey = false;
  1340. for (let i = 0; i < items.length; ++i) {
  1341. const {
  1342. key: iKey
  1343. } = items[i];
  1344. if (iKey instanceof Collection) hasCollectionKey = true;
  1345. if (doc.schema.merge && iKey && iKey.value === MERGE_KEY) {
  1346. items[i] = new Merge(items[i]);
  1347. const sources = items[i].value.items;
  1348. let error = null;
  1349. sources.some(node => {
  1350. if (node instanceof Alias) {
  1351. // During parsing, alias sources are CST nodes; to account for
  1352. // circular references their resolved values can't be used here.
  1353. const {
  1354. type
  1355. } = node.source;
  1356. if (type === PlainValue.Type.MAP || type === PlainValue.Type.FLOW_MAP) return false;
  1357. return error = 'Merge nodes aliases can only point to maps';
  1358. }
  1359. return error = 'Merge nodes can only have Alias nodes as values';
  1360. });
  1361. if (error) doc.errors.push(new PlainValue.YAMLSemanticError(cst, error));
  1362. } else {
  1363. for (let j = i + 1; j < items.length; ++j) {
  1364. const {
  1365. key: jKey
  1366. } = items[j];
  1367. if (iKey === jKey || iKey && jKey && Object.prototype.hasOwnProperty.call(iKey, 'value') && iKey.value === jKey.value) {
  1368. const msg = `Map keys must be unique; "${iKey}" is repeated`;
  1369. doc.errors.push(new PlainValue.YAMLSemanticError(cst, msg));
  1370. break;
  1371. }
  1372. }
  1373. }
  1374. }
  1375. if (hasCollectionKey && !doc.options.mapAsMap) {
  1376. const warn = 'Keys with collection values will be stringified as YAML due to JS Object restrictions. Use mapAsMap: true to avoid this.';
  1377. doc.warnings.push(new PlainValue.YAMLWarning(cst, warn));
  1378. }
  1379. cst.resolved = map;
  1380. return map;
  1381. }
  1382. const valueHasPairComment = ({
  1383. context: {
  1384. lineStart,
  1385. node,
  1386. src
  1387. },
  1388. props
  1389. }) => {
  1390. if (props.length === 0) return false;
  1391. const {
  1392. start
  1393. } = props[0];
  1394. if (node && start > node.valueRange.start) return false;
  1395. if (src[start] !== PlainValue.Char.COMMENT) return false;
  1396. for (let i = lineStart; i < start; ++i) if (src[i] === '\n') return false;
  1397. return true;
  1398. };
  1399. function resolvePairComment(item, pair) {
  1400. if (!valueHasPairComment(item)) return;
  1401. const comment = item.getPropValue(0, PlainValue.Char.COMMENT, true);
  1402. let found = false;
  1403. const cb = pair.value.commentBefore;
  1404. if (cb && cb.startsWith(comment)) {
  1405. pair.value.commentBefore = cb.substr(comment.length + 1);
  1406. found = true;
  1407. } else {
  1408. const cc = pair.value.comment;
  1409. if (!item.node && cc && cc.startsWith(comment)) {
  1410. pair.value.comment = cc.substr(comment.length + 1);
  1411. found = true;
  1412. }
  1413. }
  1414. if (found) pair.comment = comment;
  1415. }
  1416. function resolveBlockMapItems(doc, cst) {
  1417. const comments = [];
  1418. const items = [];
  1419. let key = undefined;
  1420. let keyStart = null;
  1421. for (let i = 0; i < cst.items.length; ++i) {
  1422. const item = cst.items[i];
  1423. switch (item.type) {
  1424. case PlainValue.Type.BLANK_LINE:
  1425. comments.push({
  1426. afterKey: !!key,
  1427. before: items.length
  1428. });
  1429. break;
  1430. case PlainValue.Type.COMMENT:
  1431. comments.push({
  1432. afterKey: !!key,
  1433. before: items.length,
  1434. comment: item.comment
  1435. });
  1436. break;
  1437. case PlainValue.Type.MAP_KEY:
  1438. if (key !== undefined) items.push(new Pair(key));
  1439. if (item.error) doc.errors.push(item.error);
  1440. key = resolveNode(doc, item.node);
  1441. keyStart = null;
  1442. break;
  1443. case PlainValue.Type.MAP_VALUE:
  1444. {
  1445. if (key === undefined) key = null;
  1446. if (item.error) doc.errors.push(item.error);
  1447. if (!item.context.atLineStart && item.node && item.node.type === PlainValue.Type.MAP && !item.node.context.atLineStart) {
  1448. const msg = 'Nested mappings are not allowed in compact mappings';
  1449. doc.errors.push(new PlainValue.YAMLSemanticError(item.node, msg));
  1450. }
  1451. let valueNode = item.node;
  1452. if (!valueNode && item.props.length > 0) {
  1453. // Comments on an empty mapping value need to be preserved, so we
  1454. // need to construct a minimal empty node here to use instead of the
  1455. // missing `item.node`. -- eemeli/yaml#19
  1456. valueNode = new PlainValue.PlainValue(PlainValue.Type.PLAIN, []);
  1457. valueNode.context = {
  1458. parent: item,
  1459. src: item.context.src
  1460. };
  1461. const pos = item.range.start + 1;
  1462. valueNode.range = {
  1463. start: pos,
  1464. end: pos
  1465. };
  1466. valueNode.valueRange = {
  1467. start: pos,
  1468. end: pos
  1469. };
  1470. if (typeof item.range.origStart === 'number') {
  1471. const origPos = item.range.origStart + 1;
  1472. valueNode.range.origStart = valueNode.range.origEnd = origPos;
  1473. valueNode.valueRange.origStart = valueNode.valueRange.origEnd = origPos;
  1474. }
  1475. }
  1476. const pair = new Pair(key, resolveNode(doc, valueNode));
  1477. resolvePairComment(item, pair);
  1478. items.push(pair);
  1479. if (key && typeof keyStart === 'number') {
  1480. if (item.range.start > keyStart + 1024) doc.errors.push(getLongKeyError(cst, key));
  1481. }
  1482. key = undefined;
  1483. keyStart = null;
  1484. }
  1485. break;
  1486. default:
  1487. if (key !== undefined) items.push(new Pair(key));
  1488. key = resolveNode(doc, item);
  1489. keyStart = item.range.start;
  1490. if (item.error) doc.errors.push(item.error);
  1491. next: for (let j = i + 1;; ++j) {
  1492. const nextItem = cst.items[j];
  1493. switch (nextItem && nextItem.type) {
  1494. case PlainValue.Type.BLANK_LINE:
  1495. case PlainValue.Type.COMMENT:
  1496. continue next;
  1497. case PlainValue.Type.MAP_VALUE:
  1498. break next;
  1499. default:
  1500. {
  1501. const msg = 'Implicit map keys need to be followed by map values';
  1502. doc.errors.push(new PlainValue.YAMLSemanticError(item, msg));
  1503. break next;
  1504. }
  1505. }
  1506. }
  1507. if (item.valueRangeContainsNewline) {
  1508. const msg = 'Implicit map keys need to be on a single line';
  1509. doc.errors.push(new PlainValue.YAMLSemanticError(item, msg));
  1510. }
  1511. }
  1512. }
  1513. if (key !== undefined) items.push(new Pair(key));
  1514. return {
  1515. comments,
  1516. items
  1517. };
  1518. }
  1519. function resolveFlowMapItems(doc, cst) {
  1520. const comments = [];
  1521. const items = [];
  1522. let key = undefined;
  1523. let explicitKey = false;
  1524. let next = '{';
  1525. for (let i = 0; i < cst.items.length; ++i) {
  1526. const item = cst.items[i];
  1527. if (typeof item.char === 'string') {
  1528. const {
  1529. char,
  1530. offset
  1531. } = item;
  1532. if (char === '?' && key === undefined && !explicitKey) {
  1533. explicitKey = true;
  1534. next = ':';
  1535. continue;
  1536. }
  1537. if (char === ':') {
  1538. if (key === undefined) key = null;
  1539. if (next === ':') {
  1540. next = ',';
  1541. continue;
  1542. }
  1543. } else {
  1544. if (explicitKey) {
  1545. if (key === undefined && char !== ',') key = null;
  1546. explicitKey = false;
  1547. }
  1548. if (key !== undefined) {
  1549. items.push(new Pair(key));
  1550. key = undefined;
  1551. if (char === ',') {
  1552. next = ':';
  1553. continue;
  1554. }
  1555. }
  1556. }
  1557. if (char === '}') {
  1558. if (i === cst.items.length - 1) continue;
  1559. } else if (char === next) {
  1560. next = ':';
  1561. continue;
  1562. }
  1563. const msg = `Flow map contains an unexpected ${char}`;
  1564. const err = new PlainValue.YAMLSyntaxError(cst, msg);
  1565. err.offset = offset;
  1566. doc.errors.push(err);
  1567. } else if (item.type === PlainValue.Type.BLANK_LINE) {
  1568. comments.push({
  1569. afterKey: !!key,
  1570. before: items.length
  1571. });
  1572. } else if (item.type === PlainValue.Type.COMMENT) {
  1573. checkFlowCommentSpace(doc.errors, item);
  1574. comments.push({
  1575. afterKey: !!key,
  1576. before: items.length,
  1577. comment: item.comment
  1578. });
  1579. } else if (key === undefined) {
  1580. if (next === ',') doc.errors.push(new PlainValue.YAMLSemanticError(item, 'Separator , missing in flow map'));
  1581. key = resolveNode(doc, item);
  1582. } else {
  1583. if (next !== ',') doc.errors.push(new PlainValue.YAMLSemanticError(item, 'Indicator : missing in flow map entry'));
  1584. items.push(new Pair(key, resolveNode(doc, item)));
  1585. key = undefined;
  1586. explicitKey = false;
  1587. }
  1588. }
  1589. checkFlowCollectionEnd(doc.errors, cst);
  1590. if (key !== undefined) items.push(new Pair(key));
  1591. return {
  1592. comments,
  1593. items
  1594. };
  1595. }
  1596. function resolveSeq(doc, cst) {
  1597. if (cst.type !== PlainValue.Type.SEQ && cst.type !== PlainValue.Type.FLOW_SEQ) {
  1598. const msg = `A ${cst.type} node cannot be resolved as a sequence`;
  1599. doc.errors.push(new PlainValue.YAMLSyntaxError(cst, msg));
  1600. return null;
  1601. }
  1602. const {
  1603. comments,
  1604. items
  1605. } = cst.type === PlainValue.Type.FLOW_SEQ ? resolveFlowSeqItems(doc, cst) : resolveBlockSeqItems(doc, cst);
  1606. const seq = new YAMLSeq();
  1607. seq.items = items;
  1608. resolveComments(seq, comments);
  1609. if (!doc.options.mapAsMap && items.some(it => it instanceof Pair && it.key instanceof Collection)) {
  1610. const warn = 'Keys with collection values will be stringified as YAML due to JS Object restrictions. Use mapAsMap: true to avoid this.';
  1611. doc.warnings.push(new PlainValue.YAMLWarning(cst, warn));
  1612. }
  1613. cst.resolved = seq;
  1614. return seq;
  1615. }
  1616. function resolveBlockSeqItems(doc, cst) {
  1617. const comments = [];
  1618. const items = [];
  1619. for (let i = 0; i < cst.items.length; ++i) {
  1620. const item = cst.items[i];
  1621. switch (item.type) {
  1622. case PlainValue.Type.BLANK_LINE:
  1623. comments.push({
  1624. before: items.length
  1625. });
  1626. break;
  1627. case PlainValue.Type.COMMENT:
  1628. comments.push({
  1629. comment: item.comment,
  1630. before: items.length
  1631. });
  1632. break;
  1633. case PlainValue.Type.SEQ_ITEM:
  1634. if (item.error) doc.errors.push(item.error);
  1635. items.push(resolveNode(doc, item.node));
  1636. if (item.hasProps) {
  1637. const msg = 'Sequence items cannot have tags or anchors before the - indicator';
  1638. doc.errors.push(new PlainValue.YAMLSemanticError(item, msg));
  1639. }
  1640. break;
  1641. default:
  1642. if (item.error) doc.errors.push(item.error);
  1643. doc.errors.push(new PlainValue.YAMLSyntaxError(item, `Unexpected ${item.type} node in sequence`));
  1644. }
  1645. }
  1646. return {
  1647. comments,
  1648. items
  1649. };
  1650. }
  1651. function resolveFlowSeqItems(doc, cst) {
  1652. const comments = [];
  1653. const items = [];
  1654. let explicitKey = false;
  1655. let key = undefined;
  1656. let keyStart = null;
  1657. let next = '[';
  1658. let prevItem = null;
  1659. for (let i = 0; i < cst.items.length; ++i) {
  1660. const item = cst.items[i];
  1661. if (typeof item.char === 'string') {
  1662. const {
  1663. char,
  1664. offset
  1665. } = item;
  1666. if (char !== ':' && (explicitKey || key !== undefined)) {
  1667. if (explicitKey && key === undefined) key = next ? items.pop() : null;
  1668. items.push(new Pair(key));
  1669. explicitKey = false;
  1670. key = undefined;
  1671. keyStart = null;
  1672. }
  1673. if (char === next) {
  1674. next = null;
  1675. } else if (!next && char === '?') {
  1676. explicitKey = true;
  1677. } else if (next !== '[' && char === ':' && key === undefined) {
  1678. if (next === ',') {
  1679. key = items.pop();
  1680. if (key instanceof Pair) {
  1681. const msg = 'Chaining flow sequence pairs is invalid';
  1682. const err = new PlainValue.YAMLSemanticError(cst, msg);
  1683. err.offset = offset;
  1684. doc.errors.push(err);
  1685. }
  1686. if (!explicitKey && typeof keyStart === 'number') {
  1687. const keyEnd = item.range ? item.range.start : item.offset;
  1688. if (keyEnd > keyStart + 1024) doc.errors.push(getLongKeyError(cst, key));
  1689. const {
  1690. src
  1691. } = prevItem.context;
  1692. for (let i = keyStart; i < keyEnd; ++i) if (src[i] === '\n') {
  1693. const msg = 'Implicit keys of flow sequence pairs need to be on a single line';
  1694. doc.errors.push(new PlainValue.YAMLSemanticError(prevItem, msg));
  1695. break;
  1696. }
  1697. }
  1698. } else {
  1699. key = null;
  1700. }
  1701. keyStart = null;
  1702. explicitKey = false;
  1703. next = null;
  1704. } else if (next === '[' || char !== ']' || i < cst.items.length - 1) {
  1705. const msg = `Flow sequence contains an unexpected ${char}`;
  1706. const err = new PlainValue.YAMLSyntaxError(cst, msg);
  1707. err.offset = offset;
  1708. doc.errors.push(err);
  1709. }
  1710. } else if (item.type === PlainValue.Type.BLANK_LINE) {
  1711. comments.push({
  1712. before: items.length
  1713. });
  1714. } else if (item.type === PlainValue.Type.COMMENT) {
  1715. checkFlowCommentSpace(doc.errors, item);
  1716. comments.push({
  1717. comment: item.comment,
  1718. before: items.length
  1719. });
  1720. } else {
  1721. if (next) {
  1722. const msg = `Expected a ${next} in flow sequence`;
  1723. doc.errors.push(new PlainValue.YAMLSemanticError(item, msg));
  1724. }
  1725. const value = resolveNode(doc, item);
  1726. if (key === undefined) {
  1727. items.push(value);
  1728. prevItem = item;
  1729. } else {
  1730. items.push(new Pair(key, value));
  1731. key = undefined;
  1732. }
  1733. keyStart = item.range.start;
  1734. next = ',';
  1735. }
  1736. }
  1737. checkFlowCollectionEnd(doc.errors, cst);
  1738. if (key !== undefined) items.push(new Pair(key));
  1739. return {
  1740. comments,
  1741. items
  1742. };
  1743. }
  1744. exports.Alias = Alias;
  1745. exports.Collection = Collection;
  1746. exports.Merge = Merge;
  1747. exports.Node = Node;
  1748. exports.Pair = Pair;
  1749. exports.Scalar = Scalar;
  1750. exports.YAMLMap = YAMLMap;
  1751. exports.YAMLSeq = YAMLSeq;
  1752. exports.addComment = addComment;
  1753. exports.binaryOptions = binaryOptions;
  1754. exports.boolOptions = boolOptions;
  1755. exports.findPair = findPair;
  1756. exports.intOptions = intOptions;
  1757. exports.isEmptyPath = isEmptyPath;
  1758. exports.nullOptions = nullOptions;
  1759. exports.resolveMap = resolveMap;
  1760. exports.resolveNode = resolveNode;
  1761. exports.resolveSeq = resolveSeq;
  1762. exports.resolveString = resolveString;
  1763. exports.strOptions = strOptions;
  1764. exports.stringifyNumber = stringifyNumber;
  1765. exports.stringifyString = stringifyString;
  1766. exports.toJSON = toJSON;