resolve-props.js 4.9 KB

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