resolveSeq-d03cb037.js 59 KB

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