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.

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.

Quick Links