source: frontend/node_modules/sucrase/dist/parser/plugins/jsx/index.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 2 weeks ago

Fix frontend appearance

  • Property mode set to 100644
File size: 12.1 KB
Line 
1"use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
3
4
5
6
7
8
9
10
11var _index = require('../../tokenizer/index');
12var _types = require('../../tokenizer/types');
13var _base = require('../../traverser/base');
14var _expression = require('../../traverser/expression');
15var _util = require('../../traverser/util');
16var _charcodes = require('../../util/charcodes');
17var _identifier = require('../../util/identifier');
18var _typescript = require('../typescript');
19
20/**
21 * Read token with JSX contents.
22 *
23 * In addition to detecting jsxTagStart and also regular tokens that might be
24 * part of an expression, this code detects the start and end of text ranges
25 * within JSX children. In order to properly count the number of children, we
26 * distinguish jsxText from jsxEmptyText, which is a text range that simplifies
27 * to the empty string after JSX whitespace trimming.
28 *
29 * It turns out that a JSX text range will simplify to the empty string if and
30 * only if both of these conditions hold:
31 * - The range consists entirely of whitespace characters (only counting space,
32 * tab, \r, and \n).
33 * - The range has at least one newline.
34 * This can be proven by analyzing any implementation of whitespace trimming,
35 * e.g. formatJSXTextLiteral in Sucrase or cleanJSXElementLiteralChild in Babel.
36 */
37function jsxReadToken() {
38 let sawNewline = false;
39 let sawNonWhitespace = false;
40 while (true) {
41 if (_base.state.pos >= _base.input.length) {
42 _util.unexpected.call(void 0, "Unterminated JSX contents");
43 return;
44 }
45
46 const ch = _base.input.charCodeAt(_base.state.pos);
47 if (ch === _charcodes.charCodes.lessThan || ch === _charcodes.charCodes.leftCurlyBrace) {
48 if (_base.state.pos === _base.state.start) {
49 if (ch === _charcodes.charCodes.lessThan) {
50 _base.state.pos++;
51 _index.finishToken.call(void 0, _types.TokenType.jsxTagStart);
52 return;
53 }
54 _index.getTokenFromCode.call(void 0, ch);
55 return;
56 }
57 if (sawNewline && !sawNonWhitespace) {
58 _index.finishToken.call(void 0, _types.TokenType.jsxEmptyText);
59 } else {
60 _index.finishToken.call(void 0, _types.TokenType.jsxText);
61 }
62 return;
63 }
64
65 // This is part of JSX text.
66 if (ch === _charcodes.charCodes.lineFeed) {
67 sawNewline = true;
68 } else if (ch !== _charcodes.charCodes.space && ch !== _charcodes.charCodes.carriageReturn && ch !== _charcodes.charCodes.tab) {
69 sawNonWhitespace = true;
70 }
71 _base.state.pos++;
72 }
73}
74
75function jsxReadString(quote) {
76 _base.state.pos++;
77 for (;;) {
78 if (_base.state.pos >= _base.input.length) {
79 _util.unexpected.call(void 0, "Unterminated string constant");
80 return;
81 }
82
83 const ch = _base.input.charCodeAt(_base.state.pos);
84 if (ch === quote) {
85 _base.state.pos++;
86 break;
87 }
88 _base.state.pos++;
89 }
90 _index.finishToken.call(void 0, _types.TokenType.string);
91}
92
93// Read a JSX identifier (valid tag or attribute name).
94//
95// Optimized version since JSX identifiers can't contain
96// escape characters and so can be read as single slice.
97// Also assumes that first character was already checked
98// by isIdentifierStart in readToken.
99
100function jsxReadWord() {
101 let ch;
102 do {
103 if (_base.state.pos > _base.input.length) {
104 _util.unexpected.call(void 0, "Unexpectedly reached the end of input.");
105 return;
106 }
107 ch = _base.input.charCodeAt(++_base.state.pos);
108 } while (_identifier.IS_IDENTIFIER_CHAR[ch] || ch === _charcodes.charCodes.dash);
109 _index.finishToken.call(void 0, _types.TokenType.jsxName);
110}
111
112// Parse next token as JSX identifier
113function jsxParseIdentifier() {
114 nextJSXTagToken();
115}
116
117// Parse namespaced identifier.
118function jsxParseNamespacedName(identifierRole) {
119 jsxParseIdentifier();
120 if (!_index.eat.call(void 0, _types.TokenType.colon)) {
121 // Plain identifier, so this is an access.
122 _base.state.tokens[_base.state.tokens.length - 1].identifierRole = identifierRole;
123 return;
124 }
125 // Process the second half of the namespaced name.
126 jsxParseIdentifier();
127}
128
129// Parses element name in any form - namespaced, member
130// or single identifier.
131function jsxParseElementName() {
132 const firstTokenIndex = _base.state.tokens.length;
133 jsxParseNamespacedName(_index.IdentifierRole.Access);
134 let hadDot = false;
135 while (_index.match.call(void 0, _types.TokenType.dot)) {
136 hadDot = true;
137 nextJSXTagToken();
138 jsxParseIdentifier();
139 }
140 // For tags like <div> with a lowercase letter and no dots, the name is
141 // actually *not* an identifier access, since it's referring to a built-in
142 // tag name. Remove the identifier role in this case so that it's not
143 // accidentally transformed by the imports transform when preserving JSX.
144 if (!hadDot) {
145 const firstToken = _base.state.tokens[firstTokenIndex];
146 const firstChar = _base.input.charCodeAt(firstToken.start);
147 if (firstChar >= _charcodes.charCodes.lowercaseA && firstChar <= _charcodes.charCodes.lowercaseZ) {
148 firstToken.identifierRole = null;
149 }
150 }
151}
152
153// Parses any type of JSX attribute value.
154function jsxParseAttributeValue() {
155 switch (_base.state.type) {
156 case _types.TokenType.braceL:
157 _index.next.call(void 0, );
158 _expression.parseExpression.call(void 0, );
159 nextJSXTagToken();
160 return;
161
162 case _types.TokenType.jsxTagStart:
163 jsxParseElement();
164 nextJSXTagToken();
165 return;
166
167 case _types.TokenType.string:
168 nextJSXTagToken();
169 return;
170
171 default:
172 _util.unexpected.call(void 0, "JSX value should be either an expression or a quoted JSX text");
173 }
174}
175
176// Parse JSX spread child, after already processing the {
177// Does not parse the closing }
178function jsxParseSpreadChild() {
179 _util.expect.call(void 0, _types.TokenType.ellipsis);
180 _expression.parseExpression.call(void 0, );
181}
182
183// Parses JSX opening tag starting after "<".
184// Returns true if the tag was self-closing.
185// Does not parse the last token.
186function jsxParseOpeningElement(initialTokenIndex) {
187 if (_index.match.call(void 0, _types.TokenType.jsxTagEnd)) {
188 // This is an open-fragment.
189 return false;
190 }
191 jsxParseElementName();
192 if (_base.isTypeScriptEnabled) {
193 _typescript.tsTryParseJSXTypeArgument.call(void 0, );
194 }
195 let hasSeenPropSpread = false;
196 while (!_index.match.call(void 0, _types.TokenType.slash) && !_index.match.call(void 0, _types.TokenType.jsxTagEnd) && !_base.state.error) {
197 if (_index.eat.call(void 0, _types.TokenType.braceL)) {
198 hasSeenPropSpread = true;
199 _util.expect.call(void 0, _types.TokenType.ellipsis);
200 _expression.parseMaybeAssign.call(void 0, );
201 // }
202 nextJSXTagToken();
203 continue;
204 }
205 if (
206 hasSeenPropSpread &&
207 _base.state.end - _base.state.start === 3 &&
208 _base.input.charCodeAt(_base.state.start) === _charcodes.charCodes.lowercaseK &&
209 _base.input.charCodeAt(_base.state.start + 1) === _charcodes.charCodes.lowercaseE &&
210 _base.input.charCodeAt(_base.state.start + 2) === _charcodes.charCodes.lowercaseY
211 ) {
212 _base.state.tokens[initialTokenIndex].jsxRole = _index.JSXRole.KeyAfterPropSpread;
213 }
214 jsxParseNamespacedName(_index.IdentifierRole.ObjectKey);
215 if (_index.match.call(void 0, _types.TokenType.eq)) {
216 nextJSXTagToken();
217 jsxParseAttributeValue();
218 }
219 }
220 const isSelfClosing = _index.match.call(void 0, _types.TokenType.slash);
221 if (isSelfClosing) {
222 // /
223 nextJSXTagToken();
224 }
225 return isSelfClosing;
226}
227
228// Parses JSX closing tag starting after "</".
229// Does not parse the last token.
230function jsxParseClosingElement() {
231 if (_index.match.call(void 0, _types.TokenType.jsxTagEnd)) {
232 // Fragment syntax, so we immediately have a tag end.
233 return;
234 }
235 jsxParseElementName();
236}
237
238// Parses entire JSX element, including its opening tag
239// (starting after "<"), attributes, contents and closing tag.
240// Does not parse the last token.
241function jsxParseElementAt() {
242 const initialTokenIndex = _base.state.tokens.length - 1;
243 _base.state.tokens[initialTokenIndex].jsxRole = _index.JSXRole.NoChildren;
244 let numExplicitChildren = 0;
245 const isSelfClosing = jsxParseOpeningElement(initialTokenIndex);
246 if (!isSelfClosing) {
247 nextJSXExprToken();
248 while (true) {
249 switch (_base.state.type) {
250 case _types.TokenType.jsxTagStart:
251 nextJSXTagToken();
252 if (_index.match.call(void 0, _types.TokenType.slash)) {
253 nextJSXTagToken();
254 jsxParseClosingElement();
255 // Key after prop spread takes precedence over number of children,
256 // since it means we switch to createElement, which doesn't care
257 // about number of children.
258 if (_base.state.tokens[initialTokenIndex].jsxRole !== _index.JSXRole.KeyAfterPropSpread) {
259 if (numExplicitChildren === 1) {
260 _base.state.tokens[initialTokenIndex].jsxRole = _index.JSXRole.OneChild;
261 } else if (numExplicitChildren > 1) {
262 _base.state.tokens[initialTokenIndex].jsxRole = _index.JSXRole.StaticChildren;
263 }
264 }
265 return;
266 }
267 numExplicitChildren++;
268 jsxParseElementAt();
269 nextJSXExprToken();
270 break;
271
272 case _types.TokenType.jsxText:
273 numExplicitChildren++;
274 nextJSXExprToken();
275 break;
276
277 case _types.TokenType.jsxEmptyText:
278 nextJSXExprToken();
279 break;
280
281 case _types.TokenType.braceL:
282 _index.next.call(void 0, );
283 if (_index.match.call(void 0, _types.TokenType.ellipsis)) {
284 jsxParseSpreadChild();
285 nextJSXExprToken();
286 // Spread children are a mechanism to explicitly mark children as
287 // static, so count it as 2 children to satisfy the "more than one
288 // child" condition.
289 numExplicitChildren += 2;
290 } else {
291 // If we see {}, this is an empty pseudo-expression that doesn't
292 // count as a child.
293 if (!_index.match.call(void 0, _types.TokenType.braceR)) {
294 numExplicitChildren++;
295 _expression.parseExpression.call(void 0, );
296 }
297 nextJSXExprToken();
298 }
299
300 break;
301
302 // istanbul ignore next - should never happen
303 default:
304 _util.unexpected.call(void 0, );
305 return;
306 }
307 }
308 }
309}
310
311// Parses entire JSX element from current position.
312// Does not parse the last token.
313 function jsxParseElement() {
314 nextJSXTagToken();
315 jsxParseElementAt();
316} exports.jsxParseElement = jsxParseElement;
317
318// ==================================
319// Overrides
320// ==================================
321
322 function nextJSXTagToken() {
323 _base.state.tokens.push(new (0, _index.Token)());
324 _index.skipSpace.call(void 0, );
325 _base.state.start = _base.state.pos;
326 const code = _base.input.charCodeAt(_base.state.pos);
327
328 if (_identifier.IS_IDENTIFIER_START[code]) {
329 jsxReadWord();
330 } else if (code === _charcodes.charCodes.quotationMark || code === _charcodes.charCodes.apostrophe) {
331 jsxReadString(code);
332 } else {
333 // The following tokens are just one character each.
334 ++_base.state.pos;
335 switch (code) {
336 case _charcodes.charCodes.greaterThan:
337 _index.finishToken.call(void 0, _types.TokenType.jsxTagEnd);
338 break;
339 case _charcodes.charCodes.lessThan:
340 _index.finishToken.call(void 0, _types.TokenType.jsxTagStart);
341 break;
342 case _charcodes.charCodes.slash:
343 _index.finishToken.call(void 0, _types.TokenType.slash);
344 break;
345 case _charcodes.charCodes.equalsTo:
346 _index.finishToken.call(void 0, _types.TokenType.eq);
347 break;
348 case _charcodes.charCodes.leftCurlyBrace:
349 _index.finishToken.call(void 0, _types.TokenType.braceL);
350 break;
351 case _charcodes.charCodes.dot:
352 _index.finishToken.call(void 0, _types.TokenType.dot);
353 break;
354 case _charcodes.charCodes.colon:
355 _index.finishToken.call(void 0, _types.TokenType.colon);
356 break;
357 default:
358 _util.unexpected.call(void 0, );
359 }
360 }
361} exports.nextJSXTagToken = nextJSXTagToken;
362
363function nextJSXExprToken() {
364 _base.state.tokens.push(new (0, _index.Token)());
365 _base.state.start = _base.state.pos;
366 jsxReadToken();
367}
Note: See TracBrowser for help on using the repository browser.