Simplifiy tracking of valid JSX positions (#13891)
Remove `state.inPropertyName` and simplifies `state.canStartJSXElement` tracking
This commit is contained in:
parent
de28707dfe
commit
7250d2562b
@ -22,12 +22,12 @@ import {
|
|||||||
tokenCanStartExpression,
|
tokenCanStartExpression,
|
||||||
tokenIsAssignment,
|
tokenIsAssignment,
|
||||||
tokenIsIdentifier,
|
tokenIsIdentifier,
|
||||||
tokenIsKeyword,
|
|
||||||
tokenIsKeywordOrIdentifier,
|
tokenIsKeywordOrIdentifier,
|
||||||
tokenIsOperator,
|
tokenIsOperator,
|
||||||
tokenIsPostfix,
|
tokenIsPostfix,
|
||||||
tokenIsPrefix,
|
tokenIsPrefix,
|
||||||
tokenIsRightAssociative,
|
tokenIsRightAssociative,
|
||||||
|
tokenKeywordOrIdentifierIsKeyword,
|
||||||
tokenLabelName,
|
tokenLabelName,
|
||||||
tokenOperatorPrecedence,
|
tokenOperatorPrecedence,
|
||||||
tt,
|
tt,
|
||||||
@ -2237,8 +2237,6 @@ export default class ExpressionParser extends LValParser {
|
|||||||
prop.key = this.parseMaybeAssignAllowIn();
|
prop.key = this.parseMaybeAssignAllowIn();
|
||||||
this.expect(tt.bracketR);
|
this.expect(tt.bracketR);
|
||||||
} else {
|
} else {
|
||||||
const oldInPropertyName = this.state.inPropertyName;
|
|
||||||
this.state.inPropertyName = true;
|
|
||||||
// We check if it's valid for it to be a private name when we push it.
|
// We check if it's valid for it to be a private name when we push it.
|
||||||
const type = this.state.type;
|
const type = this.state.type;
|
||||||
(prop: $FlowFixMe).key =
|
(prop: $FlowFixMe).key =
|
||||||
@ -2253,8 +2251,6 @@ export default class ExpressionParser extends LValParser {
|
|||||||
// ClassPrivateProperty is never computed, so we don't assign in that case.
|
// ClassPrivateProperty is never computed, so we don't assign in that case.
|
||||||
prop.computed = false;
|
prop.computed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.inPropertyName = oldInPropertyName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return prop.key;
|
return prop.key;
|
||||||
@ -2584,12 +2580,16 @@ export default class ExpressionParser extends LValParser {
|
|||||||
throw this.unexpected();
|
throw this.unexpected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tokenIsKeyword = tokenKeywordOrIdentifierIsKeyword(type);
|
||||||
|
|
||||||
if (liberal) {
|
if (liberal) {
|
||||||
// If the current token is not used as a keyword, set its type to "tt.name".
|
// If the current token is not used as a keyword, set its type to "tt.name".
|
||||||
// This will prevent this.next() from throwing about unexpected escapes.
|
// This will prevent this.next() from throwing about unexpected escapes.
|
||||||
this.state.type = tt.name;
|
if (tokenIsKeyword) {
|
||||||
|
this.replaceToken(tt.name);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.checkReservedWord(name, start, tokenIsKeyword(type), false);
|
this.checkReservedWord(name, start, tokenIsKeyword, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.next();
|
this.next();
|
||||||
|
|||||||
@ -21,10 +21,6 @@ import { isIdentifierChar, isIdentifierStart } from "../../util/identifier";
|
|||||||
import type { Position } from "../../util/location";
|
import type { Position } from "../../util/location";
|
||||||
import { isNewLine } from "../../util/whitespace";
|
import { isNewLine } from "../../util/whitespace";
|
||||||
import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error";
|
import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error";
|
||||||
import type { LookaheadState } from "../../tokenizer/state";
|
|
||||||
import State from "../../tokenizer/state";
|
|
||||||
|
|
||||||
type JSXLookaheadState = LookaheadState & { inPropertyName: boolean };
|
|
||||||
|
|
||||||
const HEX_NUMBER = /^[\da-fA-F]+$/;
|
const HEX_NUMBER = /^[\da-fA-F]+$/;
|
||||||
const DECIMAL_NUMBER = /^\d+$/;
|
const DECIMAL_NUMBER = /^\d+$/;
|
||||||
@ -106,7 +102,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
|||||||
case charCodes.lessThan:
|
case charCodes.lessThan:
|
||||||
case charCodes.leftCurlyBrace:
|
case charCodes.leftCurlyBrace:
|
||||||
if (this.state.pos === this.state.start) {
|
if (this.state.pos === this.state.start) {
|
||||||
if (ch === charCodes.lessThan && this.state.exprAllowed) {
|
if (ch === charCodes.lessThan && this.state.canStartJSXElement) {
|
||||||
++this.state.pos;
|
++this.state.pos;
|
||||||
return this.finishToken(tt.jsxTagStart);
|
return this.finishToken(tt.jsxTagStart);
|
||||||
}
|
}
|
||||||
@ -556,24 +552,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
|||||||
) {
|
) {
|
||||||
// In case we encounter an lt token here it will always be the start of
|
// In case we encounter an lt token here it will always be the start of
|
||||||
// jsx as the lt sign is not allowed in places that expect an expression
|
// jsx as the lt sign is not allowed in places that expect an expression
|
||||||
this.finishToken(tt.jsxTagStart);
|
this.replaceToken(tt.jsxTagStart);
|
||||||
return this.jsxParseElement();
|
return this.jsxParseElement();
|
||||||
} else {
|
} else {
|
||||||
return super.parseExprAtom(refExpressionErrors);
|
return super.parseExprAtom(refExpressionErrors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createLookaheadState(state: State): JSXLookaheadState {
|
|
||||||
const lookaheadState = ((super.createLookaheadState(
|
|
||||||
state,
|
|
||||||
): any): JSXLookaheadState);
|
|
||||||
lookaheadState.inPropertyName = state.inPropertyName;
|
|
||||||
return lookaheadState;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTokenFromCode(code: number): void {
|
getTokenFromCode(code: number): void {
|
||||||
if (this.state.inPropertyName) return super.getTokenFromCode(code);
|
|
||||||
|
|
||||||
const context = this.curContext();
|
const context = this.curContext();
|
||||||
|
|
||||||
if (context === tc.j_expr) {
|
if (context === tc.j_expr) {
|
||||||
@ -600,7 +586,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
code === charCodes.lessThan &&
|
code === charCodes.lessThan &&
|
||||||
this.state.exprAllowed &&
|
this.state.canStartJSXElement &&
|
||||||
this.input.charCodeAt(this.state.pos + 1) !== charCodes.exclamationMark
|
this.input.charCodeAt(this.state.pos + 1) !== charCodes.exclamationMark
|
||||||
) {
|
) {
|
||||||
++this.state.pos;
|
++this.state.pos;
|
||||||
@ -617,7 +603,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
|||||||
// do not consider JSX expr -> JSX open tag -> ... anymore
|
// do not consider JSX expr -> JSX open tag -> ... anymore
|
||||||
// reconsider as closing tag context
|
// reconsider as closing tag context
|
||||||
context.splice(-2, 2, tc.j_cTag);
|
context.splice(-2, 2, tc.j_cTag);
|
||||||
this.state.exprAllowed = false;
|
this.state.canStartJSXElement = false;
|
||||||
} else if (type === tt.jsxTagStart) {
|
} else if (type === tt.jsxTagStart) {
|
||||||
context.push(
|
context.push(
|
||||||
tc.j_expr, // treat as beginning of JSX expression
|
tc.j_expr, // treat as beginning of JSX expression
|
||||||
@ -627,17 +613,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
|||||||
const out = context.pop();
|
const out = context.pop();
|
||||||
if ((out === tc.j_oTag && prevType === tt.slash) || out === tc.j_cTag) {
|
if ((out === tc.j_oTag && prevType === tt.slash) || out === tc.j_cTag) {
|
||||||
context.pop();
|
context.pop();
|
||||||
this.state.exprAllowed = context[context.length - 1] === tc.j_expr;
|
this.state.canStartJSXElement =
|
||||||
|
context[context.length - 1] === tc.j_expr;
|
||||||
} else {
|
} else {
|
||||||
this.state.exprAllowed = true;
|
this.state.canStartJSXElement = true;
|
||||||
}
|
}
|
||||||
} else if (
|
|
||||||
tokenIsKeyword(type) &&
|
|
||||||
(prevType === tt.dot || prevType === tt.questionDot)
|
|
||||||
) {
|
|
||||||
this.state.exprAllowed = false;
|
|
||||||
} else {
|
} else {
|
||||||
this.state.exprAllowed = tokenComesBeforeExpression(type);
|
this.state.canStartJSXElement = tokenComesBeforeExpression(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2125,7 +2125,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
|||||||
// When ! is consumed as a postfix operator (non-null assertion),
|
// When ! is consumed as a postfix operator (non-null assertion),
|
||||||
// disallow JSX tag forming after. e.g. When parsing `p! < n.p!`
|
// disallow JSX tag forming after. e.g. When parsing `p! < n.p!`
|
||||||
// `<n.p` can not be a start of JSX tag
|
// `<n.p` can not be a start of JSX tag
|
||||||
this.state.exprAllowed = false;
|
this.state.canStartJSXElement = false;
|
||||||
this.next();
|
this.next();
|
||||||
|
|
||||||
const nonNullExpression: N.TsNonNullExpression = this.startNodeAt(
|
const nonNullExpression: N.TsNonNullExpression = this.startNodeAt(
|
||||||
|
|||||||
@ -482,7 +482,7 @@ export default class Tokenizer extends ParserErrors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Called at the end of every token. Sets `end`, `val`, and
|
// Called at the end of every token. Sets `end`, `val`, and
|
||||||
// maintains `context` and `exprAllowed`, and skips the space after
|
// maintains `context` and `canStartJSXElement`, and skips the space after
|
||||||
// the token, so that the next one's `start` will point at the
|
// the token, so that the next one's `start` will point at the
|
||||||
// right position.
|
// right position.
|
||||||
|
|
||||||
@ -498,6 +498,14 @@ export default class Tokenizer extends ParserErrors {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replaceToken(type: TokenType): void {
|
||||||
|
this.state.type = type;
|
||||||
|
// the prevType of updateContext is required
|
||||||
|
// only when the new type is tt.slash/tt.jsxTagEnd
|
||||||
|
// $FlowIgnore
|
||||||
|
this.updateContext();
|
||||||
|
}
|
||||||
|
|
||||||
// ### Token reading
|
// ### Token reading
|
||||||
|
|
||||||
// This is the function that is called to fetch the next token. It
|
// This is the function that is called to fetch the next token. It
|
||||||
|
|||||||
@ -68,7 +68,6 @@ export default class State {
|
|||||||
maybeInArrowParameters: boolean = false;
|
maybeInArrowParameters: boolean = false;
|
||||||
inType: boolean = false;
|
inType: boolean = false;
|
||||||
noAnonFunctionType: boolean = false;
|
noAnonFunctionType: boolean = false;
|
||||||
inPropertyName: boolean = false;
|
|
||||||
hasFlowComment: boolean = false;
|
hasFlowComment: boolean = false;
|
||||||
isAmbientContext: boolean = false;
|
isAmbientContext: boolean = false;
|
||||||
inAbstractClass: boolean = false;
|
inAbstractClass: boolean = false;
|
||||||
@ -127,7 +126,7 @@ export default class State {
|
|||||||
// or ends a string template
|
// or ends a string template
|
||||||
context: Array<TokContext> = [ct.brace];
|
context: Array<TokContext> = [ct.brace];
|
||||||
// Used to track whether a JSX element is allowed to form
|
// Used to track whether a JSX element is allowed to form
|
||||||
exprAllowed: boolean = true;
|
canStartJSXElement: boolean = true;
|
||||||
|
|
||||||
// Used to signal to callers of `readWord1` whether the word
|
// Used to signal to callers of `readWord1` whether the word
|
||||||
// contained any escape sequences. This is needed because words with
|
// contained any escape sequences. This is needed because words with
|
||||||
|
|||||||
@ -332,6 +332,12 @@ export function tokenIsIdentifier(token: TokenType): boolean {
|
|||||||
return token >= tt._as && token <= tt.name;
|
return token >= tt._as && token <= tt.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function tokenKeywordOrIdentifierIsKeyword(token: TokenType): boolean {
|
||||||
|
// we can remove the token >= tt._in check when we
|
||||||
|
// know a token is either keyword or identifier
|
||||||
|
return token <= tt._while;
|
||||||
|
}
|
||||||
|
|
||||||
export function tokenIsKeywordOrIdentifier(token: TokenType): boolean {
|
export function tokenIsKeywordOrIdentifier(token: TokenType): boolean {
|
||||||
return token >= tt._in && token <= tt.name;
|
return token >= tt._in && token <= tt.name;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user