import { eat, finishToken, getTokenFromCode, IdentifierRole, match, next, skipSpace, Token, } from "../../tokenizer/index"; import {TokenType as tt} from "../../tokenizer/types"; import {input, isTypeScriptEnabled, state} from "../../traverser/base"; import {parseExpression, parseMaybeAssign} from "../../traverser/expression"; import {expect, unexpected} from "../../traverser/util"; import {charCodes} from "../../util/charcodes"; import {IS_IDENTIFIER_CHAR, IS_IDENTIFIER_START} from "../../util/identifier"; import {tsTryParseJSXTypeArgument} from "../typescript"; // Reads inline JSX contents token. function jsxReadToken() { for (;;) { if (state.pos >= input.length) { unexpected("Unterminated JSX contents"); return; } const ch = input.charCodeAt(state.pos); switch (ch) { case charCodes.lessThan: case charCodes.leftCurlyBrace: if (state.pos === state.start) { if (ch === charCodes.lessThan) { state.pos++; finishToken(tt.jsxTagStart); return; } getTokenFromCode(ch); return; } finishToken(tt.jsxText); return; default: state.pos++; } } } function jsxReadString(quote) { state.pos++; for (;;) { if (state.pos >= input.length) { unexpected("Unterminated string constant"); return; } const ch = input.charCodeAt(state.pos); if (ch === quote) { state.pos++; break; } state.pos++; } finishToken(tt.string); } // Read a JSX identifier (valid tag or attribute name). // // Optimized version since JSX identifiers can't contain // escape characters and so can be read as single slice. // Also assumes that first character was already checked // by isIdentifierStart in readToken. function jsxReadWord() { let ch; do { if (state.pos > input.length) { unexpected("Unexpectedly reached the end of input."); return; } ch = input.charCodeAt(++state.pos); } while (IS_IDENTIFIER_CHAR[ch] || ch === charCodes.dash); finishToken(tt.jsxName); } // Parse next token as JSX identifier function jsxParseIdentifier() { nextJSXTagToken(); } // Parse namespaced identifier. function jsxParseNamespacedName(identifierRole) { jsxParseIdentifier(); if (!eat(tt.colon)) { // Plain identifier, so this is an access. state.tokens[state.tokens.length - 1].identifierRole = identifierRole; return; } // Process the second half of the namespaced name. jsxParseIdentifier(); } // Parses element name in any form - namespaced, member // or single identifier. function jsxParseElementName() { jsxParseNamespacedName(IdentifierRole.Access); while (match(tt.dot)) { nextJSXTagToken(); jsxParseIdentifier(); } } // Parses any type of JSX attribute value. function jsxParseAttributeValue() { switch (state.type) { case tt.braceL: next(); jsxParseExpressionContainer(); nextJSXTagToken(); return; case tt.jsxTagStart: jsxParseElement(); nextJSXTagToken(); return; case tt.string: nextJSXTagToken(); return; default: unexpected("JSX value should be either an expression or a quoted JSX text"); } } function jsxParseEmptyExpression() { // Do nothing. } // Parse JSX spread child, after already processing the { // Does not parse the closing } function jsxParseSpreadChild() { expect(tt.ellipsis); parseExpression(); } // Parses JSX expression enclosed into curly brackets, after already processing the { // Does not parse the closing } function jsxParseExpressionContainer() { if (match(tt.braceR)) { jsxParseEmptyExpression(); } else { parseExpression(); } } // Parses following JSX attribute name-value pair. function jsxParseAttribute() { if (eat(tt.braceL)) { expect(tt.ellipsis); parseMaybeAssign(); // } nextJSXTagToken(); return; } jsxParseNamespacedName(IdentifierRole.ObjectKey); if (match(tt.eq)) { nextJSXTagToken(); jsxParseAttributeValue(); } } // Parses JSX opening tag starting after "<". // Returns true if the tag was self-closing. // Does not parse the last token. function jsxParseOpeningElement() { if (match(tt.jsxTagEnd)) { // This is an open-fragment. return false; } jsxParseElementName(); if (isTypeScriptEnabled) { tsTryParseJSXTypeArgument(); } while (!match(tt.slash) && !match(tt.jsxTagEnd) && !state.error) { jsxParseAttribute(); } const isSelfClosing = match(tt.slash); if (isSelfClosing) { // / nextJSXTagToken(); } return isSelfClosing; } // Parses JSX closing tag starting after "