resolve-props.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. 'use strict';
  2. function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnNewline }) {
  3. let spaceBefore = false;
  4. let atNewline = startOnNewline;
  5. let hasSpace = startOnNewline;
  6. let comment = '';
  7. let commentSep = '';
  8. let hasNewline = false;
  9. let hasNewlineAfterProp = false;
  10. let reqSpace = false;
  11. let anchor = null;
  12. let tag = null;
  13. let comma = null;
  14. let found = null;
  15. let start = null;
  16. for (const token of tokens) {
  17. if (reqSpace) {
  18. if (token.type !== 'space' &&
  19. token.type !== 'newline' &&
  20. token.type !== 'comma')
  21. onError(token.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
  22. reqSpace = false;
  23. }
  24. switch (token.type) {
  25. case 'space':
  26. // At the doc level, tabs at line start may be parsed
  27. // as leading white space rather than indentation.
  28. // In a flow collection, only the parser handles indent.
  29. if (!flow &&
  30. atNewline &&
  31. indicator !== 'doc-start' &&
  32. token.source[0] === '\t')
  33. onError(token, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
  34. hasSpace = true;
  35. break;
  36. case 'comment': {
  37. if (!hasSpace)
  38. onError(token, 'MISSING_CHAR', 'Comments must be separated from other tokens by white space characters');
  39. const cb = token.source.substring(1) || ' ';
  40. if (!comment)
  41. comment = cb;
  42. else
  43. comment += commentSep + cb;
  44. commentSep = '';
  45. atNewline = false;
  46. break;
  47. }
  48. case 'newline':
  49. if (atNewline) {
  50. if (comment)
  51. comment += token.source;
  52. else
  53. spaceBefore = true;
  54. }
  55. else
  56. commentSep += token.source;
  57. atNewline = true;
  58. hasNewline = true;
  59. if (anchor || tag)
  60. hasNewlineAfterProp = true;
  61. hasSpace = true;
  62. break;
  63. case 'anchor':
  64. if (anchor)
  65. onError(token, 'MULTIPLE_ANCHORS', 'A node can have at most one anchor');
  66. if (token.source.endsWith(':'))
  67. onError(token.offset + token.source.length - 1, 'BAD_ALIAS', 'Anchor ending in : is ambiguous', true);
  68. anchor = token;
  69. if (start === null)
  70. start = token.offset;
  71. atNewline = false;
  72. hasSpace = false;
  73. reqSpace = true;
  74. break;
  75. case 'tag': {
  76. if (tag)
  77. onError(token, 'MULTIPLE_TAGS', 'A node can have at most one tag');
  78. tag = token;
  79. if (start === null)
  80. start = token.offset;
  81. atNewline = false;
  82. hasSpace = false;
  83. reqSpace = true;
  84. break;
  85. }
  86. case indicator:
  87. // Could here handle preceding comments differently
  88. if (anchor || tag)
  89. onError(token, 'BAD_PROP_ORDER', `Anchors and tags must be after the ${token.source} indicator`);
  90. if (found)
  91. onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow ?? 'collection'}`);
  92. found = token;
  93. atNewline = false;
  94. hasSpace = false;
  95. break;
  96. case 'comma':
  97. if (flow) {
  98. if (comma)
  99. onError(token, 'UNEXPECTED_TOKEN', `Unexpected , in ${flow}`);
  100. comma = token;
  101. atNewline = false;
  102. hasSpace = false;
  103. break;
  104. }
  105. // else fallthrough
  106. default:
  107. onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.type} token`);
  108. atNewline = false;
  109. hasSpace = false;
  110. }
  111. }
  112. const last = tokens[tokens.length - 1];
  113. const end = last ? last.offset + last.source.length : offset;
  114. if (reqSpace &&
  115. next &&
  116. next.type !== 'space' &&
  117. next.type !== 'newline' &&
  118. next.type !== 'comma' &&
  119. (next.type !== 'scalar' || next.source !== ''))
  120. onError(next.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
  121. return {
  122. comma,
  123. found,
  124. spaceBefore,
  125. comment,
  126. hasNewline,
  127. hasNewlineAfterProp,
  128. anchor,
  129. tag,
  130. end,
  131. start: start ?? end
  132. };
  133. }
  134. exports.resolveProps = resolveProps;