15. JSX
15.1. JSX Support
N4JS scripts with file extension "n4jsx" support the special JSX syntax. Files ending with "jsx" are considered as plain Javascript.
Req. IDE-241101: React Component (ver. 1)
There are two ways of defining a React component:
-
functional component: A function is a React component, iff
-
its return type is React.Element
-
the function has at least one parameter. This first parameter is considered as the
props
property. The type of the first parameter only has public fields (or methods defined by Object). The fields may be defined as optional.
-
-
class component: A class is a React component, iff
-
it extends
React.Component
. -
the type parameters must be structural types.
-
The first parameter defines the
props
type, it must only define public fields (or methods defined by Object) -
The second parameter defines the
state
type, it must only define public fields (or methods defined by Object)
-
-
In both cases, the name must be capitalized (i.e., start with an upper case letter).
Component must be written capitalized (i.e. first letter upper case), html (or other xml elements) must be written lower case.
Req. IDE-241102: JSX Basic Syntax (ver. 1)
The following JSX syntax is supported (EBNF with model element assignments as used in Xtext). All other ECMAScript 2015 and N4JS syntax constructs are unaffected by this addition.
PrimaryExpression <Yield> returns n4js::Expression:
... // as in N4JS
| JSXElement
;
JSXElement:
'<' jsxElementName=JSXElementName JSXAttributes
(('>' jsxChildren+=JSXChild* JSXClosingElement) | ('/' '>'));
fragment JSXClosingElement *:
'<' '/' jsxClosingName=JSXElementName '>';
JSXChild:
JSXElement | JSXExpression
;
JSXExpression: '{' expression=AssignmentExpression<false,false> '}';
JSXElementName:
expression=JSXElementNameExpression
;
JSXElementNameExpression returns n4js::Expression:
IdentifierRef<false>
({n4js::ParameterizedPropertyAccessExpression.target=current} ParameterizedPropertyAccessExpressionTail<false>)*
;
fragment JSXAttributes *:
jsxAttributes+=JSXAttribute*;
JSXAttribute:
JSXSpreadAttribute
|
JSXPropertyAttribute;
JSXSpreadAttribute:
'{' '...' expression=AssignmentExpression<false,false> '}';
JSXPropertyAttribute:
property=[types::IdentifiableElement|IdentifierName] '=' (jsxAttributeValue=StringLiteral | ('{'
jsxAttributeValue=AssignmentExpression<false,false> '}'));
This syntax is similar to the syntax defined by JSX, except that
-
JSXNamedspaceName is not supported
-
JSXText is not supported, instead, string template literals are to be used
Remarks on differences between the syntax defined in [Req-IDE-241102] and JSX:
-
JSXSelfClosingElement, JSXOpeningElement and JSXClosingElement are merged into one single rule JSXElement
-
JSXAttributes is defined as fragment, that is, the attributes become fields of the JSXElement
-
The different types of JSXElementName are defined by means of JSXElementNameExpression which is a simple expression, reusing the existing rules IdentifierRef and ParameterizedPropertyAccessExpression to model JSXIdentifer and JSXMemberExpression respectively
-
JSXPropertyAttribute models the JSX non-spread JSXAttribute definition; again existing rules (IdentifierName, StringLiteral) are used to model JSX specific ones (JSXAttributeName, JSXDoubleStringCharacters, JSXSingleStringCharacters)
Req. IDE-241401: JSX Syntax With Free Text (ver. 1)
JSXChild:
JSXElement | JSXExpression | JSXText
;
Req. IDE-241103: JSX Extended Syntax Check (ver. 1)
-
It is an error, if corresponding opening and closing JSXElements have different names.
Req. IDE-241113: JSX Expression Type (ver. 1)
The type of a JSX expression is React.Element.
Req. IDE-241114: React Symbol (ver. 1)
If a JSX literal is used, it is an error if the React symbol is not imported via
import React from "react"
Req. IDE-241115: JSXElement names (tags) (ver. 1)
-
It is an error if a capitalized tag cannot be bound to a function or class declaration.
Req. IDE-241116: JSXElements referring to React components (ver. 1)
-
It is an error, if a tag binds to a function declaration, which is not conform to the functional component definition.
-
It is an error, if a tag binds to a class declaration, which is not conform to the class component definition.
Req. IDE-241117: JSXAttributes and React component properties (ver. 1)
If the tag binds to a component, the following constraints must hold:
-
The attribute must be a non-private field of the properties type.
-
The tag should define attributes for all non-optional fields of the properties type. If no attribute is defined for a non-optional field, a warning is issued.
-
The type of the attribute expression must be conform to the type of the corresponding
props
's property
Req. IDE-241118: JSXElements referring to XML elements (ver. 1)
If the lower-case tag does not bind to a function or class declaration, the following constraints must be hold:
-
If the tag is neither a pre-defined HTML tag nor an SVG tag, a warning is issued.
-
If an attribute of the tag is not a pre-defined property of the html tag or react specific attributes, a warning is issued. This requirement is currently NOT supported.
Req. IDE-241119: JSXSpreadAttribute behavior (ver. 1)
The use of spread operators within an JSX element for specifying multiple attributes should be allowed. In this case, all constraints regarding type conformity checking and non-optional properties mentioned in [Req-IDE-241117] apply to the attributes specified in the spread operator. In particular,
-
The type of each attribute specified in spread operator must be conform to the type of the corresponding property of
props
. -
If a non-optional property of
props
is specified neither as attribute nor in a spread operator, a warning is issued.
15.2. JSX Backend
The support for JSX in N4JS aims for an implementation that adheres to the idea of React JSX. This means that JSX elements are transpiled to React Element factory calls (e.g. <div prop="c">content</div>
transpiles to React.createElement('div', {prop: "c"}, null)
). For that, the transpiler must be aware of a specific implementation of React and the corresponding createElement
function.
Req. GH-687: React Implementation (ver. 1)
A react implementation is given in terms of a module that fulfils the following properties:
-
The FQN of the module is
react
. -
Type definitions are available for the module.
-
The module exports a function of name
createElement
.
If a react implementation is declared as project dependency, the N4JS transpiler automatically imports it to the module using JSX and generates the corresponding factory calls.