forked from facebook/jsx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
spec.emu
364 lines (291 loc) · 14 KB
/
spec.emu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
<!doctype html>
<meta charset="utf8">
<link rel="stylesheet" href="./spec.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/github.min.css">
<script src="./spec.js"></script>
<pre class="metadata">
title: JSX
version: Draft
contributors: Meta Platform Inc.
copyright: false
</pre>
<emu-intro id="sec-intro">
<h1>Introduction</h1>
<p>JSX is an XML-like syntax extension to ECMAScript without any defined semantics. It's NOT intended to be implemented by engines or browsers. <b>It's NOT a proposal to incorporate JSX into the ECMAScript spec itself.</b> It's intended to be used by various preprocessors (transpilers) to transform these tokens into standard ECMAScript.
</p>
<!-- recommend using a HTML escape/unescape tool -->
<pre><code class="language-jsx">
// Using JSX to express UI components
var dropdown =
<Dropdown>
A dropdown list
<Menu>
<MenuItem>Do Something</MenuItem>
<MenuItem>Do Something Fun!</MenuItem>
<MenuItem>Do Something Else</MenuItem>
</Menu>
</Dropdown>;
render(dropdown);
</code></pre>
<emu-intro id="sec-rationale">
<h1>Rationale</h1>
<p>The purpose of this specification is to define a concise and familiar syntax for defining tree structures with attributes. A generic but well defined syntax enables a community of independent parsers and syntax highlighters to conform to a single specification.</p>
<p>Embedding a new syntax in an existing language is a risky venture. Other syntax implementors or the existing language may introduce another incompatible syntax extension. </p>
<p> Through a stand-alone specification, we make it easier for implementors of other syntax extensions to consider JSX when designing their own syntax. This will hopefully allow various new syntax extensions to co-exist. </p>
<p> It is our intention to claim minimal syntactic real estate while keeping the syntax concise and familiar. That way we leave the door open for other extensions.</p>
<p> This specification does not attempt to comply with any XML or HTML specification. JSX is designed as an ECMAScript feature and the similarity to XML is only for familiarity.</p>
</emu-intro>
</emu-intro>
<emu-clause id="sec-jsx">
<h1>JSX Definition</h1>
<emu-clause id="sec-jsx-PrimaryExpression">
<h1>Modified Productions</h1>
<p>JSX extends the |PrimaryExpression| in the <a href="https://tc39.es/ecma262/">ECMAScript (ECMA-262)</a> grammar:</p>
<h2>Syntax</h2>
<emu-grammar>
PrimaryExpression :
<ins>JSXElement</ins>
<ins>JSXFragment</ins>
</emu-grammar>
</emu-clause>
<emu-clause id="sec-jsx-elements">
<h1>JSX Elements</h1>
<h2>Syntax</h2>
<emu-grammar type="definition">
JSXElement :
JSXSelfClosingElement
JSXOpeningElement JSXChildren? JSXClosingElement
JSXSelfClosingElement :
`<` JSXElementName JSXAttributes? `/` `>`
JSXOpeningElement :
`<` JSXElementName JSXAttributes? `>`
JSXClosingElement :
`<` `/` JSXElementName `>`
JSXFragment :
`<` `>` JSXChildren? `<` `/` `>`
JSXElementName :
JSXIdentifier
JSXNamespacedName
JSXMemberExpression
JSXIdentifier :
IdentifierStart
JSXIdentifier IdentifierPart
JSXIdentifier [no one of WhiteSpace or Comment here] `-`
</emu-grammar>
<emu-note>
The grammar of |JSXIdentifier| is not parameterized the same way |Identifier| is. This means that <code><await /></code> is still a valid production when "[await]" appears during the derivation.
</emu-note>
<emu-grammar type="definition">
JSXNamespacedName :
JSXIdentifier `:` JSXIdentifier
JSXMemberExpression :
JSXIdentifier `.` JSXIdentifier
JSXMemberExpression `.` JSXIdentifier
</emu-grammar>
<emu-clause type="sdo" id="sec-jsx-elements-early-errors">
<h1>Static Semantics: Early Errors</h1>
<emu-grammar>
JSXElement : JSXOpeningElement JSXChildren? JSXClosingElement
</emu-grammar>
<ul>
<li>It is a Syntax Error if the source text matched by the |JSXElementName| of |JSXOpeningElement| does not equal to the source text matched by the |JSXElementName| of |JSXClosingElement|.</li>
</ul>
</emu-clause>
</emu-clause>
<emu-clause id="sec-jsx-attributes">
<h1>JSX Attributes</h1>
<h2>Syntax</h2>
<emu-grammar type="definition">
JSXAttributes :
JSXSpreadAttribute JSXAttributes?
JSXAttribute JSXAttributes?
JSXSpreadAttribute :
`{` `...` AssignmentExpression `}`
JSXAttribute :
JSXAttributeName JSXAttributeInitializer?
JSXAttributeName :
JSXIdentifier
JSXNamespacedName
JSXAttributeInitializer :
`=` JSXAttributeValue
JSXAttributeValue :
`"` JSXDoubleStringCharacters? `"`
`'` JSXSingleStringCharacters? `'`
`{` AssignmentExpression `}`
JSXElement
JSXFragment
JSXDoubleStringCharacters ::
JSXDoubleStringCharacter JSXDoubleStringCharacters?
JSXDoubleStringCharacter ::
JSXStringCharacter but not `"`
JSXSingleStringCharacters ::
JSXSingleStringCharacter JSXSingleStringCharacters?
JSXSingleStringCharacter ::
JSXStringCharacter but not `'`
</emu-grammar>
</emu-clause>
<emu-clause id="sec-jsx-children">
<h1>JSX Children</h1>
<h2>Syntax</h2>
<emu-grammar type="definition">
JSXChildren :
JSXChild JSXChildren?
JSXChild :
JSXText
JSXElement
JSXFragment
`{` JSXChildExpression? `}`
JSXText ::
JSXTextCharacter JSXText?
JSXTextCharacter ::
JSXStringCharacter but not one of `{` or `<` or `>` or `}`
JSXChildExpression :
AssignmentExpression
`...` AssignmentExpression
</emu-grammar>
</emu-clause>
<emu-clause id="sec-jsx-string">
<h1>JSX Strings</h1>
<emu-clause id="sec-jsx-string-characters">
<h1>JSX String Characters</h1>
<p>Historically, string characters within |JSXAttributeValue| and |JSXText| are extended to allow the presence of <emu-xref href="#sec-HTMLCharacterReference">HTML character references</emu-xref> to make copy-pasting between HTML and JSX easier, at the cost of not supporting `\\` |EscapeSequence| of ECMAScript's |StringLiteral|. We may revisit this decision in the future.</p>
<h2>Syntax</h2>
<emu-grammar type="definition">
JSXStringCharacter ::
SourceCharacter but not one of HTMLCharacterReference
</emu-grammar>
</emu-clause>
<emu-clause id="sec-HTMLCharacterReference">
<h1>HTML Character References</h1>
<emu-note>
<p>The grammars here followed <a href="https://html.spec.whatwg.org/multipage/syntax.html#character-references">the living HTML standard</a>.</p>
</emu-note>
<h2>Syntax</h2>
<emu-grammar type="definition">
HTMLCharacterReference::
HTMLDecimalCharacterReference
HTMLHexCharacterReference
HTMLNamedCharacterReference
HTMLDecimalCharacterReference::
`&` `#` DecimalDigits [> but only if MV of |DecimalDigits| ≦ 0x10FFFF] `;`
HTMLHexCharacterReference::
`&` `#` `x` HexDigits [> but only if MV of |HexDigits| ≦ 0x10FFFF] `;`
HTMLNamedCharacterReference::
`&` HTMLNamedCharacterReferenceName `;`
HTMLNamedCharacterReferenceName::
> List of 252 character entity references defined in HTML4 standard
</emu-grammar>
<emu-note>
<p>
This means that the later expansion of this list to <a href="https://html.spec.whatwg.org/multipage/named-characters.html#named-character-references">2231 named character references</a> since HTML5 are intentionally <i>excluded</i> from the support.
</p>
<p>
The list of 252 character entity references defined in HTML4 standard can be found <a href="https://www.w3.org/TR/1999/REC-html401-19991224/sgml/entities.html">at here</a>.
</p>
</emu-note>
</emu-clause>
<emu-clause id="sec-jsx-JSXString">
<h1>JSX String Definition</h1>
<h2>Syntax</h2>
<emu-grammar type="definition">
JSXString :
JSXDoubleStringCharacters
JSXSingleStringCharacters
JSXText
</emu-grammar>
<emu-clause type="sdo" id="sec-jsx-JSXString-SV">
<h1>Static Semantics: SV</h1>
<p>An implementation may convert |JSXString| into a ECMAScript String value. In order to do this, JSX extends the syntax-directed operation <a href="https://tc39.es/ecma262/#sec-static-semantics-sv">SV</a> to |JSXStringCharacter|:</p>
<ins class="block">
<ul>
<li>
The SV of <emu-grammar> JSXStringCharacter :: SourceCharacter </emu-grammar> is the result of performing <a href="https://tc39.es/ecma262/#sec-utf16encodecodepoint">UTF16EncodeCodePoint</a> on the code point matched by |SourceCharacter|.
</li>
<li>
The SV of <emu-grammar> HTMLDecimalCharacterReference:: `&` `#` DecimalDigits `;` </emu-grammar> is the same as the result of performing <a href="https://tc39.es/ecma262/#sec-utf16encodecodepoint">UTF16EncodeCodePoint</a> on the MV of |DecimalDigits|
</li>
<li>
The SV of <emu-grammar>HTMLHexCharacterReference:: `&` `#` `x` HexDigits `;` </emu-grammar> is the same as the result of performing <a href="https://tc39.es/ecma262/#sec-utf16encodecodepoint">UTF16EncodeCodePoint</a> on the MV of |HexDigits|
</li>
<li>
The SV of <emu-grammar>HTMLNamedCharacterReference:: `&` HTMLNamedCharacterReferenceName `;` </emu-grammar> is the same as the result of performing <a href="https://tc39.es/ecma262/#sec-utf16encodecodepoint">UTF16EncodeCodePoint</a> on the code points matched by |HTMLNamedCharacterReferenceName| according the implementation-defined list of names.
</li>
</ul>
</ins>
<p>The exact approach to fulfill the extended sematics is <a href="https://tc39.es/ecma262/#implementation-defined">implementation-defined</a>.</p>
<emu-note>
For example, at the time of writing, Babel implemented this in its <a href="https://github.com/babel/babel/blob/main/packages/babel-parser/src/plugins/jsx/index.js">JSX parser</a>, while TypeScript implemented it via a <a href="https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformers/jsx.ts">JSX transformer</a>.
</emu-note>
</emu-clause>
</emu-clause>
</emu-clause>
</emu-clause>
<emu-annex id="sec-why-not-template-literals">
<h1>Why not Template Literals?</h1>
<p><a href="https://www.ecma-international.org/ecma-262/8.0/index.html">ECMAScript 6th Edition (ECMA-262)</a> introduces template literals which are intended to be used for embedding DSL in ECMAScript. Why not just use that instead of inventing a syntax that's not part of ECMAScript?</p>
<p>Template literals work well for long embedded DSLs. Unfortunately the syntax noise is substantial when you exit in and out of embedded arbitrary ECMAScript expressions with identifiers in scope.</p>
<pre><code class="language-jsx">
// Template Literals
var box = jsx`
<${Box}>
${
shouldShowAnswer(user) ?
jsx`<${Answer} value=${false}>no</${Answer}>` :
jsx`
<${Box.Comment}>
Text Content
</${Box.Comment}>
`
}
</${Box}>
`;
</code></pre>
<p>It would be possible to use template literals as a syntactic entry point and change the semantics inside the template literal to allow embedded scripts that can be evaluated in scope:</p>
<pre><code class="language-jsx">
// Template Literals with embedded JSX
var box = jsx`
<Box>
{
shouldShowAnswer(user) ?
<Answer value={false}>no</Answer> :
<Box.Comment>
Text Content
</Box.Comment>
}
</Box>
`;
</code></pre>
<p>However, this would lead to further divergence. Tooling that is built around the assumptions imposed by template literals wouldn't work. It would undermine the meaning of template literals. It would be necessary to define how JSX behaves within the rest of the ECMAScript grammar within the template literal anyway.</p>
<p>Therefore it's better to introduce JSX as an entirely new type of PrimaryExpression:</p>
<pre><code class="language-jsx">
// JSX
var box =
<Box>
{
shouldShowAnswer(user) ?
<Answer value={false}>no</Answer> :
<Box.Comment>
Text Content
</Box.Comment>
}
</Box>;
</code></pre>
<emu-note>
Don't you love the syntax highlighting here? ;)
</emu-note>
</emu-annex>
<emu-annex id="sec-why-not-JXON">
<h1>Why not JXON?</h1>
<p>Another alternative would be to use object initializers (similar to <a href="https://htmlpreview.github.io/?https://github.com/mdn/archived-content/blob/main/files/en-us/archive/jxon/raw.html">JXON</a>). Unfortunately, the balanced braces do not give great syntactic hints for where an element starts and ends in large trees. Balanced named tags is a critical syntactic feature of the XML-style notation.</p>
</emu-annex>
<emu-annex id="sec-prior-art">
<h1>Prior Art</h1>
<p>The JSX syntax is similar to the <a href="http://www.ecma-international.org/publications/files/ECMA-ST-WITHDRAWN/Ecma-357.pdf">E4X Specification (ECMA-357)</a>. E4X is a deprecated specification with deep reaching semantic meaning. JSX partially overlaps with a tiny subset of the E4X syntax. However, JSX has no relation to the E4X specification.</p>
</emu-annex>
<emu-annex id="sec-license">
<h1>License</h1>
<p>Copyright (c) 2014 - present, <a href="https://about.facebook.com/meta/">Meta Platforms, Inc.</a> All rights reserved.</p>
<p>
This work is licensed under a <a href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
</p>
</emu-annex>