309 lines
8.8 KiB
JavaScript
309 lines
8.8 KiB
JavaScript
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
var _index = require('../../tokenizer/index');
|
||
|
var _types = require('../../tokenizer/types');
|
||
|
var _base = require('../../traverser/base');
|
||
|
var _expression = require('../../traverser/expression');
|
||
|
var _util = require('../../traverser/util');
|
||
|
var _charcodes = require('../../util/charcodes');
|
||
|
var _identifier = require('../../util/identifier');
|
||
|
var _typescript = require('../typescript');
|
||
|
|
||
|
// Reads inline JSX contents token.
|
||
|
function jsxReadToken() {
|
||
|
for (;;) {
|
||
|
if (_base.state.pos >= _base.input.length) {
|
||
|
_util.unexpected.call(void 0, "Unterminated JSX contents");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const ch = _base.input.charCodeAt(_base.state.pos);
|
||
|
|
||
|
switch (ch) {
|
||
|
case _charcodes.charCodes.lessThan:
|
||
|
case _charcodes.charCodes.leftCurlyBrace:
|
||
|
if (_base.state.pos === _base.state.start) {
|
||
|
if (ch === _charcodes.charCodes.lessThan) {
|
||
|
_base.state.pos++;
|
||
|
_index.finishToken.call(void 0, _types.TokenType.jsxTagStart);
|
||
|
return;
|
||
|
}
|
||
|
_index.getTokenFromCode.call(void 0, ch);
|
||
|
return;
|
||
|
}
|
||
|
_index.finishToken.call(void 0, _types.TokenType.jsxText);
|
||
|
return;
|
||
|
|
||
|
default:
|
||
|
_base.state.pos++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function jsxReadString(quote) {
|
||
|
_base.state.pos++;
|
||
|
for (;;) {
|
||
|
if (_base.state.pos >= _base.input.length) {
|
||
|
_util.unexpected.call(void 0, "Unterminated string constant");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const ch = _base.input.charCodeAt(_base.state.pos);
|
||
|
if (ch === quote) {
|
||
|
_base.state.pos++;
|
||
|
break;
|
||
|
}
|
||
|
_base.state.pos++;
|
||
|
}
|
||
|
_index.finishToken.call(void 0, _types.TokenType.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 (_base.state.pos > _base.input.length) {
|
||
|
_util.unexpected.call(void 0, "Unexpectedly reached the end of input.");
|
||
|
return;
|
||
|
}
|
||
|
ch = _base.input.charCodeAt(++_base.state.pos);
|
||
|
} while (_identifier.IS_IDENTIFIER_CHAR[ch] || ch === _charcodes.charCodes.dash);
|
||
|
_index.finishToken.call(void 0, _types.TokenType.jsxName);
|
||
|
}
|
||
|
|
||
|
// Parse next token as JSX identifier
|
||
|
function jsxParseIdentifier() {
|
||
|
nextJSXTagToken();
|
||
|
}
|
||
|
|
||
|
// Parse namespaced identifier.
|
||
|
function jsxParseNamespacedName(identifierRole) {
|
||
|
jsxParseIdentifier();
|
||
|
if (!_index.eat.call(void 0, _types.TokenType.colon)) {
|
||
|
// Plain identifier, so this is an access.
|
||
|
_base.state.tokens[_base.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(_index.IdentifierRole.Access);
|
||
|
while (_index.match.call(void 0, _types.TokenType.dot)) {
|
||
|
nextJSXTagToken();
|
||
|
jsxParseIdentifier();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Parses any type of JSX attribute value.
|
||
|
function jsxParseAttributeValue() {
|
||
|
switch (_base.state.type) {
|
||
|
case _types.TokenType.braceL:
|
||
|
_index.next.call(void 0, );
|
||
|
jsxParseExpressionContainer();
|
||
|
nextJSXTagToken();
|
||
|
return;
|
||
|
|
||
|
case _types.TokenType.jsxTagStart:
|
||
|
jsxParseElement();
|
||
|
nextJSXTagToken();
|
||
|
return;
|
||
|
|
||
|
case _types.TokenType.string:
|
||
|
nextJSXTagToken();
|
||
|
return;
|
||
|
|
||
|
default:
|
||
|
_util.unexpected.call(void 0, "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() {
|
||
|
_util.expect.call(void 0, _types.TokenType.ellipsis);
|
||
|
_expression.parseExpression.call(void 0, );
|
||
|
}
|
||
|
|
||
|
// Parses JSX expression enclosed into curly brackets, after already processing the {
|
||
|
// Does not parse the closing }
|
||
|
function jsxParseExpressionContainer() {
|
||
|
if (_index.match.call(void 0, _types.TokenType.braceR)) {
|
||
|
jsxParseEmptyExpression();
|
||
|
} else {
|
||
|
_expression.parseExpression.call(void 0, );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Parses following JSX attribute name-value pair.
|
||
|
function jsxParseAttribute() {
|
||
|
if (_index.eat.call(void 0, _types.TokenType.braceL)) {
|
||
|
_util.expect.call(void 0, _types.TokenType.ellipsis);
|
||
|
_expression.parseMaybeAssign.call(void 0, );
|
||
|
// }
|
||
|
nextJSXTagToken();
|
||
|
return;
|
||
|
}
|
||
|
jsxParseNamespacedName(_index.IdentifierRole.ObjectKey);
|
||
|
if (_index.match.call(void 0, _types.TokenType.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 (_index.match.call(void 0, _types.TokenType.jsxTagEnd)) {
|
||
|
// This is an open-fragment.
|
||
|
return false;
|
||
|
}
|
||
|
jsxParseElementName();
|
||
|
if (_base.isTypeScriptEnabled) {
|
||
|
_typescript.tsTryParseJSXTypeArgument.call(void 0, );
|
||
|
}
|
||
|
while (!_index.match.call(void 0, _types.TokenType.slash) && !_index.match.call(void 0, _types.TokenType.jsxTagEnd) && !_base.state.error) {
|
||
|
jsxParseAttribute();
|
||
|
}
|
||
|
const isSelfClosing = _index.match.call(void 0, _types.TokenType.slash);
|
||
|
if (isSelfClosing) {
|
||
|
// /
|
||
|
nextJSXTagToken();
|
||
|
}
|
||
|
return isSelfClosing;
|
||
|
}
|
||
|
|
||
|
// Parses JSX closing tag starting after "</".
|
||
|
// Does not parse the last token.
|
||
|
function jsxParseClosingElement() {
|
||
|
if (_index.match.call(void 0, _types.TokenType.jsxTagEnd)) {
|
||
|
// Fragment syntax, so we immediately have a tag end.
|
||
|
return;
|
||
|
}
|
||
|
jsxParseElementName();
|
||
|
}
|
||
|
|
||
|
// Parses entire JSX element, including its opening tag
|
||
|
// (starting after "<"), attributes, contents and closing tag.
|
||
|
// Does not parse the last token.
|
||
|
function jsxParseElementAt() {
|
||
|
const isSelfClosing = jsxParseOpeningElement();
|
||
|
if (!isSelfClosing) {
|
||
|
nextJSXExprToken();
|
||
|
while (true) {
|
||
|
switch (_base.state.type) {
|
||
|
case _types.TokenType.jsxTagStart:
|
||
|
nextJSXTagToken();
|
||
|
if (_index.match.call(void 0, _types.TokenType.slash)) {
|
||
|
nextJSXTagToken();
|
||
|
jsxParseClosingElement();
|
||
|
return;
|
||
|
}
|
||
|
jsxParseElementAt();
|
||
|
nextJSXExprToken();
|
||
|
break;
|
||
|
|
||
|
case _types.TokenType.jsxText:
|
||
|
nextJSXExprToken();
|
||
|
break;
|
||
|
|
||
|
case _types.TokenType.braceL:
|
||
|
_index.next.call(void 0, );
|
||
|
if (_index.match.call(void 0, _types.TokenType.ellipsis)) {
|
||
|
jsxParseSpreadChild();
|
||
|
nextJSXExprToken();
|
||
|
} else {
|
||
|
jsxParseExpressionContainer();
|
||
|
nextJSXExprToken();
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
// istanbul ignore next - should never happen
|
||
|
default:
|
||
|
_util.unexpected.call(void 0, );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Parses entire JSX element from current position.
|
||
|
// Does not parse the last token.
|
||
|
function jsxParseElement() {
|
||
|
nextJSXTagToken();
|
||
|
jsxParseElementAt();
|
||
|
} exports.jsxParseElement = jsxParseElement;
|
||
|
|
||
|
// ==================================
|
||
|
// Overrides
|
||
|
// ==================================
|
||
|
|
||
|
function nextJSXTagToken() {
|
||
|
_base.state.tokens.push(new (0, _index.Token)());
|
||
|
_index.skipSpace.call(void 0, );
|
||
|
_base.state.start = _base.state.pos;
|
||
|
const code = _base.input.charCodeAt(_base.state.pos);
|
||
|
|
||
|
if (_identifier.IS_IDENTIFIER_START[code]) {
|
||
|
jsxReadWord();
|
||
|
} else if (code === _charcodes.charCodes.quotationMark || code === _charcodes.charCodes.apostrophe) {
|
||
|
jsxReadString(code);
|
||
|
} else {
|
||
|
// The following tokens are just one character each.
|
||
|
++_base.state.pos;
|
||
|
switch (code) {
|
||
|
case _charcodes.charCodes.greaterThan:
|
||
|
_index.finishToken.call(void 0, _types.TokenType.jsxTagEnd);
|
||
|
break;
|
||
|
case _charcodes.charCodes.lessThan:
|
||
|
_index.finishToken.call(void 0, _types.TokenType.jsxTagStart);
|
||
|
break;
|
||
|
case _charcodes.charCodes.slash:
|
||
|
_index.finishToken.call(void 0, _types.TokenType.slash);
|
||
|
break;
|
||
|
case _charcodes.charCodes.equalsTo:
|
||
|
_index.finishToken.call(void 0, _types.TokenType.eq);
|
||
|
break;
|
||
|
case _charcodes.charCodes.leftCurlyBrace:
|
||
|
_index.finishToken.call(void 0, _types.TokenType.braceL);
|
||
|
break;
|
||
|
case _charcodes.charCodes.dot:
|
||
|
_index.finishToken.call(void 0, _types.TokenType.dot);
|
||
|
break;
|
||
|
case _charcodes.charCodes.colon:
|
||
|
_index.finishToken.call(void 0, _types.TokenType.colon);
|
||
|
break;
|
||
|
default:
|
||
|
_util.unexpected.call(void 0, );
|
||
|
}
|
||
|
}
|
||
|
} exports.nextJSXTagToken = nextJSXTagToken;
|
||
|
|
||
|
function nextJSXExprToken() {
|
||
|
_base.state.tokens.push(new (0, _index.Token)());
|
||
|
_base.state.start = _base.state.pos;
|
||
|
jsxReadToken();
|
||
|
}
|