• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*@internal*/
2namespace ts {
3    export function transformJsx(context: TransformationContext) {
4        interface PerFileState {
5            importSpecifier?: string;
6            filenameDeclaration?: VariableDeclaration & { name: Identifier; };
7            utilizedImplicitRuntimeImports?: Map<Map<ImportSpecifier>>;
8        }
9
10        const {
11            factory,
12            getEmitHelperFactory: emitHelpers,
13        } = context;
14        const compilerOptions = context.getCompilerOptions();
15        let currentSourceFile: SourceFile;
16        let currentFileState!: PerFileState;
17
18        return chainBundle(context, transformSourceFile);
19
20        function getCurrentFileNameExpression(): Identifier {
21            if (currentFileState.filenameDeclaration) {
22                return currentFileState.filenameDeclaration.name;
23            }
24            const declaration = factory.createVariableDeclaration(factory.createUniqueName("_jsxFileName", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), /*exclaimationToken*/ undefined, /*type*/ undefined, factory.createStringLiteral(currentSourceFile.fileName));
25            currentFileState.filenameDeclaration = declaration as VariableDeclaration & { name: Identifier };
26            return currentFileState.filenameDeclaration.name;
27        }
28
29        function getJsxFactoryCalleePrimitive(childrenLength: number): "jsx" | "jsxs" | "jsxDEV" {
30            return compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsxDEV" : childrenLength > 1 ? "jsxs" : "jsx";
31        }
32
33        function getJsxFactoryCallee(childrenLength: number) {
34            const type = getJsxFactoryCalleePrimitive(childrenLength);
35            return getImplicitImportForName(type);
36        }
37
38        function getImplicitJsxFragmentReference() {
39            return getImplicitImportForName("Fragment");
40        }
41
42        function getImplicitImportForName(name: string) {
43            const importSource = name === "createElement"
44                ? currentFileState.importSpecifier!
45                : getJSXRuntimeImport(currentFileState.importSpecifier, compilerOptions)!;
46            const existing = currentFileState.utilizedImplicitRuntimeImports?.get(importSource)?.get(name);
47            if (existing) {
48                return existing.name;
49            }
50            if (!currentFileState.utilizedImplicitRuntimeImports) {
51                currentFileState.utilizedImplicitRuntimeImports = createMap();
52            }
53            let specifierSourceImports = currentFileState.utilizedImplicitRuntimeImports.get(importSource);
54            if (!specifierSourceImports) {
55                specifierSourceImports = createMap();
56                currentFileState.utilizedImplicitRuntimeImports.set(importSource, specifierSourceImports);
57            }
58            const generatedName = factory.createUniqueName(`_${name}`, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel | GeneratedIdentifierFlags.AllowNameSubstitution);
59            const specifier = factory.createImportSpecifier(factory.createIdentifier(name), generatedName);
60            generatedName.generatedImportReference = specifier;
61            specifierSourceImports.set(name, specifier);
62            return generatedName;
63        }
64
65        /**
66         * Transform JSX-specific syntax in a SourceFile.
67         *
68         * @param node A SourceFile node.
69         */
70        function transformSourceFile(node: SourceFile) {
71            if (node.isDeclarationFile) {
72                return node;
73            }
74
75            currentSourceFile = node;
76            currentFileState = {};
77            currentFileState.importSpecifier = getJSXImplicitImportBase(compilerOptions, node);
78            let visited = visitEachChild(node, visitor, context);
79            addEmitHelpers(visited, context.readEmitHelpers());
80            let statements: readonly Statement[] = visited.statements;
81            if (currentFileState.filenameDeclaration) {
82                statements = insertStatementAfterCustomPrologue(statements.slice(), factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([currentFileState.filenameDeclaration], NodeFlags.Const)));
83            }
84            if (currentFileState.utilizedImplicitRuntimeImports) {
85                for (const [importSource, importSpecifiersMap] of arrayFrom(currentFileState.utilizedImplicitRuntimeImports.entries())) {
86                    if (isExternalModule(node)) {
87                        // Add `import` statement
88                        const importStatement = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, factory.createImportClause(/*typeOnly*/ false, /*name*/ undefined, factory.createNamedImports(arrayFrom(importSpecifiersMap.values()))), factory.createStringLiteral(importSource));
89                        setParentRecursive(importStatement, /*incremental*/ false);
90                        statements = insertStatementAfterCustomPrologue(statements.slice(), importStatement);
91                    }
92                    else if (isExternalOrCommonJsModule(node)) {
93                        // Add `require` statement
94                        const requireStatement = factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([
95                            factory.createVariableDeclaration(
96                                factory.createObjectBindingPattern(map(arrayFrom(importSpecifiersMap.values()), s => factory.createBindingElement(/*dotdotdot*/ undefined, s.propertyName, s.name))),
97                                /*exclaimationToken*/ undefined,
98                                /*type*/ undefined,
99                                factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, [factory.createStringLiteral(importSource)])
100                            )
101                        ], NodeFlags.Const));
102                        setParentRecursive(requireStatement, /*incremental*/ false);
103                        statements = insertStatementAfterCustomPrologue(statements.slice(), requireStatement);
104                    }
105                    else {
106                        // Do nothing (script file) - consider an error in the checker?
107                    }
108                }
109            }
110            if (statements !== visited.statements) {
111                visited = factory.updateSourceFile(visited, statements);
112            }
113            currentFileState = undefined!;
114            return visited;
115        }
116
117        function visitor(node: Node): VisitResult<Node> {
118            if (node.transformFlags & TransformFlags.ContainsJsx) {
119                return visitorWorker(node);
120            }
121            else {
122                return node;
123            }
124        }
125
126        function visitorWorker(node: Node): VisitResult<Node> {
127            switch (node.kind) {
128                case SyntaxKind.JsxElement:
129                    return visitJsxElement(<JsxElement>node, /*isChild*/ false);
130
131                case SyntaxKind.JsxSelfClosingElement:
132                    return visitJsxSelfClosingElement(<JsxSelfClosingElement>node, /*isChild*/ false);
133
134                case SyntaxKind.JsxFragment:
135                    return visitJsxFragment(<JsxFragment>node, /*isChild*/ false);
136
137                case SyntaxKind.JsxExpression:
138                    return visitJsxExpression(<JsxExpression>node);
139
140                default:
141                    return visitEachChild(node, visitor, context);
142            }
143        }
144
145        function transformJsxChildToExpression(node: JsxChild): Expression | undefined {
146            switch (node.kind) {
147                case SyntaxKind.JsxText:
148                    return visitJsxText(node);
149
150                case SyntaxKind.JsxExpression:
151                    return visitJsxExpression(node);
152
153                case SyntaxKind.JsxElement:
154                    return visitJsxElement(node, /*isChild*/ true);
155
156                case SyntaxKind.JsxSelfClosingElement:
157                    return visitJsxSelfClosingElement(node, /*isChild*/ true);
158
159                case SyntaxKind.JsxFragment:
160                    return visitJsxFragment(node, /*isChild*/ true);
161
162                default:
163                    return Debug.failBadSyntaxKind(node);
164            }
165        }
166
167        /**
168         * The react jsx/jsxs transform falls back to `createElement` when an explicit `key` argument comes after a spread
169         */
170        function hasKeyAfterPropsSpread(node: JsxOpeningLikeElement) {
171            let spread = false;
172            for (const elem of node.attributes.properties) {
173                if (isJsxSpreadAttribute(elem)) {
174                    spread = true;
175                }
176                else if (spread && isJsxAttribute(elem) && elem.name.escapedText === "key") {
177                    return true;
178                }
179            }
180            return false;
181        }
182
183        function shouldUseCreateElement(node: JsxOpeningLikeElement) {
184            return currentFileState.importSpecifier === undefined || hasKeyAfterPropsSpread(node);
185        }
186
187        function visitJsxElement(node: JsxElement, isChild: boolean) {
188            const tagTransform = shouldUseCreateElement(node.openingElement) ? visitJsxOpeningLikeElementCreateElement : visitJsxOpeningLikeElementJSX;
189            return tagTransform(node.openingElement, node.children, isChild, /*location*/ node);
190        }
191
192        function visitJsxSelfClosingElement(node: JsxSelfClosingElement, isChild: boolean) {
193            const tagTransform = shouldUseCreateElement(node) ? visitJsxOpeningLikeElementCreateElement : visitJsxOpeningLikeElementJSX;
194            return tagTransform(node, /*children*/ undefined, isChild, /*location*/ node);
195        }
196
197        function visitJsxFragment(node: JsxFragment, isChild: boolean) {
198            const tagTransform = currentFileState.importSpecifier === undefined ? visitJsxOpeningFragmentCreateElement : visitJsxOpeningFragmentJSX;
199            return tagTransform(node.openingFragment, node.children, isChild, /*location*/ node);
200        }
201
202        function convertJsxChildrenToChildrenPropObject(children: readonly JsxChild[]) {
203            const nonWhitespaceChildren = getSemanticJsxChildren(children);
204            if (length(nonWhitespaceChildren) === 1) {
205                const result = transformJsxChildToExpression(nonWhitespaceChildren[0]);
206                return result && factory.createObjectLiteralExpression([
207                    factory.createPropertyAssignment("children", result)
208                ]);
209            }
210            const result = mapDefined(children, transformJsxChildToExpression);
211            return !result.length ? undefined : factory.createObjectLiteralExpression([
212                factory.createPropertyAssignment("children", factory.createArrayLiteralExpression(result))
213            ]);
214        }
215
216        function visitJsxOpeningLikeElementJSX(node: JsxOpeningLikeElement, children: readonly JsxChild[] | undefined, isChild: boolean, location: TextRange) {
217            const tagName = getTagName(node);
218            let objectProperties: Expression;
219            const keyAttr = find(node.attributes.properties, p => !!p.name && isIdentifier(p.name) && p.name.escapedText === "key") as JsxAttribute | undefined;
220            const attrs = keyAttr ? filter(node.attributes.properties, p => p !== keyAttr) : node.attributes.properties;
221
222            let segments: Expression[] = [];
223            if (attrs.length) {
224                // Map spans of JsxAttribute nodes into object literals and spans
225                // of JsxSpreadAttribute nodes into expressions.
226                segments = flatten(
227                    spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread
228                        ? map(attrs, transformJsxSpreadAttributeToExpression)
229                        : factory.createObjectLiteralExpression(map(attrs, transformJsxAttributeToObjectLiteralElement))
230                    )
231                );
232
233                if (isJsxSpreadAttribute(attrs[0])) {
234                    // We must always emit at least one object literal before a spread
235                    // argument.factory.createObjectLiteral
236                    segments.unshift(factory.createObjectLiteralExpression());
237                }
238            }
239            if (children && children.length) {
240                const result = convertJsxChildrenToChildrenPropObject(children);
241                if (result) {
242                    segments.push(result);
243                }
244            }
245
246            if (segments.length === 0) {
247                objectProperties = factory.createObjectLiteralExpression([]);
248                // When there are no attributes, React wants {}
249            }
250            else {
251                // Either emit one big object literal (no spread attribs), or
252                // a call to the __assign helper.
253                objectProperties = singleOrUndefined(segments) || emitHelpers().createAssignHelper(segments);
254            }
255
256            return visitJsxOpeningLikeElementOrFragmentJSX(tagName, objectProperties, keyAttr, length(getSemanticJsxChildren(children || emptyArray)), isChild, location);
257        }
258
259        function visitJsxOpeningLikeElementOrFragmentJSX(tagName: Expression, objectProperties: Expression, keyAttr: JsxAttribute | undefined, childrenLength: number, isChild: boolean, location: TextRange) {
260            const args: Expression[] = [tagName, objectProperties, !keyAttr ? factory.createVoidZero() : transformJsxAttributeInitializer(keyAttr.initializer)];
261            if (compilerOptions.jsx === JsxEmit.ReactJSXDev) {
262                const originalFile = getOriginalNode(currentSourceFile);
263                if (originalFile && isSourceFile(originalFile)) {
264                    // isStaticChildren development flag
265                    args.push(childrenLength > 1 ? factory.createTrue() : factory.createFalse());
266                    // __source development flag
267                    const lineCol = getLineAndCharacterOfPosition(originalFile, location.pos);
268                    args.push(factory.createObjectLiteralExpression([
269                        factory.createPropertyAssignment("fileName", getCurrentFileNameExpression()),
270                        factory.createPropertyAssignment("lineNumber", factory.createNumericLiteral(lineCol.line + 1)),
271                        factory.createPropertyAssignment("columnNumber", factory.createNumericLiteral(lineCol.character + 1))
272                    ]));
273                    // __self development flag
274                    args.push(factory.createThis());
275                }
276            }
277            const element = setTextRange(factory.createCallExpression(getJsxFactoryCallee(childrenLength), /*typeArguments*/ undefined, args), location);
278
279            if (isChild) {
280                startOnNewLine(element);
281            }
282
283            return element;
284        }
285
286        function visitJsxOpeningLikeElementCreateElement(node: JsxOpeningLikeElement, children: readonly JsxChild[] | undefined, isChild: boolean, location: TextRange) {
287            const tagName = getTagName(node);
288            let objectProperties: Expression | undefined;
289            const attrs = node.attributes.properties;
290            if (attrs.length === 0) {
291                objectProperties = factory.createNull();
292                // When there are no attributes, React wants "null"
293            }
294            else {
295                // Map spans of JsxAttribute nodes into object literals and spans
296                // of JsxSpreadAttribute nodes into expressions.
297                const segments = flatten<Expression | ObjectLiteralExpression>(
298                    spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread
299                        ? map(attrs, transformJsxSpreadAttributeToExpression)
300                        : factory.createObjectLiteralExpression(map(attrs, transformJsxAttributeToObjectLiteralElement))
301                    )
302                );
303
304                if (isJsxSpreadAttribute(attrs[0])) {
305                    // We must always emit at least one object literal before a spread
306                    // argument.factory.createObjectLiteral
307                    segments.unshift(factory.createObjectLiteralExpression());
308                }
309
310                // Either emit one big object literal (no spread attribs), or
311                // a call to the __assign helper.
312                objectProperties = singleOrUndefined(segments);
313                if (!objectProperties) {
314                    objectProperties = emitHelpers().createAssignHelper(segments);
315                }
316            }
317
318            const callee = currentFileState.importSpecifier === undefined
319                ? createJsxFactoryExpression(
320                    factory,
321                    context.getEmitResolver().getJsxFactoryEntity(currentSourceFile),
322                    compilerOptions.reactNamespace!, // TODO: GH#18217
323                    node
324                )
325                : getImplicitImportForName("createElement");
326
327            const element = createExpressionForJsxElement(
328                factory,
329                callee,
330                tagName,
331                objectProperties,
332                mapDefined(children, transformJsxChildToExpression),
333                location
334            );
335
336            if (isChild) {
337                startOnNewLine(element);
338            }
339
340            return element;
341        }
342
343        function visitJsxOpeningFragmentJSX(_node: JsxOpeningFragment, children: readonly JsxChild[], isChild: boolean, location: TextRange) {
344            let childrenProps: Expression | undefined;
345            if (children && children.length) {
346                const result = convertJsxChildrenToChildrenPropObject(children);
347                if (result) {
348                    childrenProps = result;
349                }
350            }
351            return visitJsxOpeningLikeElementOrFragmentJSX(
352                getImplicitJsxFragmentReference(),
353                childrenProps || factory.createObjectLiteralExpression([]),
354                /*keyAttr*/ undefined,
355                length(getSemanticJsxChildren(children)),
356                isChild,
357                location
358            );
359        }
360
361        function visitJsxOpeningFragmentCreateElement(node: JsxOpeningFragment, children: readonly JsxChild[], isChild: boolean, location: TextRange) {
362            const element = createExpressionForJsxFragment(
363                factory,
364                context.getEmitResolver().getJsxFactoryEntity(currentSourceFile),
365                context.getEmitResolver().getJsxFragmentFactoryEntity(currentSourceFile),
366                compilerOptions.reactNamespace!, // TODO: GH#18217
367                mapDefined(children, transformJsxChildToExpression),
368                node,
369                location
370            );
371
372            if (isChild) {
373                startOnNewLine(element);
374            }
375
376            return element;
377        }
378
379        function transformJsxSpreadAttributeToExpression(node: JsxSpreadAttribute) {
380            return visitNode(node.expression, visitor, isExpression);
381        }
382
383        function transformJsxAttributeToObjectLiteralElement(node: JsxAttribute) {
384            const name = getAttributeName(node);
385            const expression = transformJsxAttributeInitializer(node.initializer);
386            return factory.createPropertyAssignment(name, expression);
387        }
388
389        function transformJsxAttributeInitializer(node: StringLiteral | JsxExpression | undefined): Expression {
390            if (node === undefined) {
391                return factory.createTrue();
392            }
393            else if (node.kind === SyntaxKind.StringLiteral) {
394                // Always recreate the literal to escape any escape sequences or newlines which may be in the original jsx string and which
395                // Need to be escaped to be handled correctly in a normal string
396                const singleQuote = node.singleQuote !== undefined ? node.singleQuote : !isStringDoubleQuoted(node, currentSourceFile);
397                const literal = factory.createStringLiteral(tryDecodeEntities(node.text) || node.text, singleQuote);
398                return setTextRange(literal, node);
399            }
400            else if (node.kind === SyntaxKind.JsxExpression) {
401                if (node.expression === undefined) {
402                    return factory.createTrue();
403                }
404                return visitNode(node.expression, visitor, isExpression);
405            }
406            else {
407                return Debug.failBadSyntaxKind(node);
408            }
409        }
410
411        function visitJsxText(node: JsxText): StringLiteral | undefined {
412            const fixed = fixupWhitespaceAndDecodeEntities(node.text);
413            return fixed === undefined ? undefined : factory.createStringLiteral(fixed);
414        }
415
416        /**
417         * JSX trims whitespace at the end and beginning of lines, except that the
418         * start/end of a tag is considered a start/end of a line only if that line is
419         * on the same line as the closing tag. See examples in
420         * tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx
421         * See also https://www.w3.org/TR/html4/struct/text.html#h-9.1 and https://www.w3.org/TR/CSS2/text.html#white-space-model
422         *
423         * An equivalent algorithm would be:
424         * - If there is only one line, return it.
425         * - If there is only whitespace (but multiple lines), return `undefined`.
426         * - Split the text into lines.
427         * - 'trimRight' the first line, 'trimLeft' the last line, 'trim' middle lines.
428         * - Decode entities on each line (individually).
429         * - Remove empty lines and join the rest with " ".
430         */
431        function fixupWhitespaceAndDecodeEntities(text: string): string | undefined {
432            let acc: string | undefined;
433            // First non-whitespace character on this line.
434            let firstNonWhitespace = 0;
435            // Last non-whitespace character on this line.
436            let lastNonWhitespace = -1;
437            // These initial values are special because the first line is:
438            // firstNonWhitespace = 0 to indicate that we want leading whitsepace,
439            // but lastNonWhitespace = -1 as a special flag to indicate that we *don't* include the line if it's all whitespace.
440
441            for (let i = 0; i < text.length; i++) {
442                const c = text.charCodeAt(i);
443                if (isLineBreak(c)) {
444                    // If we've seen any non-whitespace characters on this line, add the 'trim' of the line.
445                    // (lastNonWhitespace === -1 is a special flag to detect whether the first line is all whitespace.)
446                    if (firstNonWhitespace !== -1 && lastNonWhitespace !== -1) {
447                        acc = addLineOfJsxText(acc, text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1));
448                    }
449
450                    // Reset firstNonWhitespace for the next line.
451                    // Don't bother to reset lastNonWhitespace because we ignore it if firstNonWhitespace = -1.
452                    firstNonWhitespace = -1;
453                }
454                else if (!isWhiteSpaceSingleLine(c)) {
455                    lastNonWhitespace = i;
456                    if (firstNonWhitespace === -1) {
457                        firstNonWhitespace = i;
458                    }
459                }
460            }
461
462            return firstNonWhitespace !== -1
463                // Last line had a non-whitespace character. Emit the 'trimLeft', meaning keep trailing whitespace.
464                ? addLineOfJsxText(acc, text.substr(firstNonWhitespace))
465                // Last line was all whitespace, so ignore it
466                : acc;
467        }
468
469        function addLineOfJsxText(acc: string | undefined, trimmedLine: string): string {
470            // We do not escape the string here as that is handled by the printer
471            // when it emits the literal. We do, however, need to decode JSX entities.
472            const decoded = decodeEntities(trimmedLine);
473            return acc === undefined ? decoded : acc + " " + decoded;
474        }
475
476        /**
477         * Replace entities like "&nbsp;", "&#123;", and "&#xDEADBEEF;" with the characters they encode.
478         * See https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
479         */
480        function decodeEntities(text: string): string {
481            return text.replace(/&((#((\d+)|x([\da-fA-F]+)))|(\w+));/g, (match, _all, _number, _digits, decimal, hex, word) => {
482                if (decimal) {
483                    return utf16EncodeAsString(parseInt(decimal, 10));
484                }
485                else if (hex) {
486                    return utf16EncodeAsString(parseInt(hex, 16));
487                }
488                else {
489                    const ch = entities.get(word);
490                    // If this is not a valid entity, then just use `match` (replace it with itself, i.e. don't replace)
491                    return ch ? utf16EncodeAsString(ch) : match;
492                }
493            });
494        }
495
496        /** Like `decodeEntities` but returns `undefined` if there were no entities to decode. */
497        function tryDecodeEntities(text: string): string | undefined {
498            const decoded = decodeEntities(text);
499            return decoded === text ? undefined : decoded;
500        }
501
502        function getTagName(node: JsxElement | JsxOpeningLikeElement): Expression {
503            if (node.kind === SyntaxKind.JsxElement) {
504                return getTagName(node.openingElement);
505            }
506            else {
507                const name = node.tagName;
508                if (isIdentifier(name) && isIntrinsicJsxName(name.escapedText)) {
509                    return factory.createStringLiteral(idText(name));
510                }
511                else {
512                    return createExpressionFromEntityName(factory, name);
513                }
514            }
515        }
516
517        /**
518         * Emit an attribute name, which is quoted if it needs to be quoted. Because
519         * these emit into an object literal property name, we don't need to be worried
520         * about keywords, just non-identifier characters
521         */
522        function getAttributeName(node: JsxAttribute): StringLiteral | Identifier {
523            const name = node.name;
524            const text = idText(name);
525            if (/^[A-Za-z_]\w*$/.test(text)) {
526                return name;
527            }
528            else {
529                return factory.createStringLiteral(text);
530            }
531        }
532
533        function visitJsxExpression(node: JsxExpression) {
534            return visitNode(node.expression, visitor, isExpression);
535        }
536    }
537
538    const entities = new Map(getEntries({
539        quot: 0x0022,
540        amp: 0x0026,
541        apos: 0x0027,
542        lt: 0x003C,
543        gt: 0x003E,
544        nbsp: 0x00A0,
545        iexcl: 0x00A1,
546        cent: 0x00A2,
547        pound: 0x00A3,
548        curren: 0x00A4,
549        yen: 0x00A5,
550        brvbar: 0x00A6,
551        sect: 0x00A7,
552        uml: 0x00A8,
553        copy: 0x00A9,
554        ordf: 0x00AA,
555        laquo: 0x00AB,
556        not: 0x00AC,
557        shy: 0x00AD,
558        reg: 0x00AE,
559        macr: 0x00AF,
560        deg: 0x00B0,
561        plusmn: 0x00B1,
562        sup2: 0x00B2,
563        sup3: 0x00B3,
564        acute: 0x00B4,
565        micro: 0x00B5,
566        para: 0x00B6,
567        middot: 0x00B7,
568        cedil: 0x00B8,
569        sup1: 0x00B9,
570        ordm: 0x00BA,
571        raquo: 0x00BB,
572        frac14: 0x00BC,
573        frac12: 0x00BD,
574        frac34: 0x00BE,
575        iquest: 0x00BF,
576        Agrave: 0x00C0,
577        Aacute: 0x00C1,
578        Acirc: 0x00C2,
579        Atilde: 0x00C3,
580        Auml: 0x00C4,
581        Aring: 0x00C5,
582        AElig: 0x00C6,
583        Ccedil: 0x00C7,
584        Egrave: 0x00C8,
585        Eacute: 0x00C9,
586        Ecirc: 0x00CA,
587        Euml: 0x00CB,
588        Igrave: 0x00CC,
589        Iacute: 0x00CD,
590        Icirc: 0x00CE,
591        Iuml: 0x00CF,
592        ETH: 0x00D0,
593        Ntilde: 0x00D1,
594        Ograve: 0x00D2,
595        Oacute: 0x00D3,
596        Ocirc: 0x00D4,
597        Otilde: 0x00D5,
598        Ouml: 0x00D6,
599        times: 0x00D7,
600        Oslash: 0x00D8,
601        Ugrave: 0x00D9,
602        Uacute: 0x00DA,
603        Ucirc: 0x00DB,
604        Uuml: 0x00DC,
605        Yacute: 0x00DD,
606        THORN: 0x00DE,
607        szlig: 0x00DF,
608        agrave: 0x00E0,
609        aacute: 0x00E1,
610        acirc: 0x00E2,
611        atilde: 0x00E3,
612        auml: 0x00E4,
613        aring: 0x00E5,
614        aelig: 0x00E6,
615        ccedil: 0x00E7,
616        egrave: 0x00E8,
617        eacute: 0x00E9,
618        ecirc: 0x00EA,
619        euml: 0x00EB,
620        igrave: 0x00EC,
621        iacute: 0x00ED,
622        icirc: 0x00EE,
623        iuml: 0x00EF,
624        eth: 0x00F0,
625        ntilde: 0x00F1,
626        ograve: 0x00F2,
627        oacute: 0x00F3,
628        ocirc: 0x00F4,
629        otilde: 0x00F5,
630        ouml: 0x00F6,
631        divide: 0x00F7,
632        oslash: 0x00F8,
633        ugrave: 0x00F9,
634        uacute: 0x00FA,
635        ucirc: 0x00FB,
636        uuml: 0x00FC,
637        yacute: 0x00FD,
638        thorn: 0x00FE,
639        yuml: 0x00FF,
640        OElig: 0x0152,
641        oelig: 0x0153,
642        Scaron: 0x0160,
643        scaron: 0x0161,
644        Yuml: 0x0178,
645        fnof: 0x0192,
646        circ: 0x02C6,
647        tilde: 0x02DC,
648        Alpha: 0x0391,
649        Beta: 0x0392,
650        Gamma: 0x0393,
651        Delta: 0x0394,
652        Epsilon: 0x0395,
653        Zeta: 0x0396,
654        Eta: 0x0397,
655        Theta: 0x0398,
656        Iota: 0x0399,
657        Kappa: 0x039A,
658        Lambda: 0x039B,
659        Mu: 0x039C,
660        Nu: 0x039D,
661        Xi: 0x039E,
662        Omicron: 0x039F,
663        Pi: 0x03A0,
664        Rho: 0x03A1,
665        Sigma: 0x03A3,
666        Tau: 0x03A4,
667        Upsilon: 0x03A5,
668        Phi: 0x03A6,
669        Chi: 0x03A7,
670        Psi: 0x03A8,
671        Omega: 0x03A9,
672        alpha: 0x03B1,
673        beta: 0x03B2,
674        gamma: 0x03B3,
675        delta: 0x03B4,
676        epsilon: 0x03B5,
677        zeta: 0x03B6,
678        eta: 0x03B7,
679        theta: 0x03B8,
680        iota: 0x03B9,
681        kappa: 0x03BA,
682        lambda: 0x03BB,
683        mu: 0x03BC,
684        nu: 0x03BD,
685        xi: 0x03BE,
686        omicron: 0x03BF,
687        pi: 0x03C0,
688        rho: 0x03C1,
689        sigmaf: 0x03C2,
690        sigma: 0x03C3,
691        tau: 0x03C4,
692        upsilon: 0x03C5,
693        phi: 0x03C6,
694        chi: 0x03C7,
695        psi: 0x03C8,
696        omega: 0x03C9,
697        thetasym: 0x03D1,
698        upsih: 0x03D2,
699        piv: 0x03D6,
700        ensp: 0x2002,
701        emsp: 0x2003,
702        thinsp: 0x2009,
703        zwnj: 0x200C,
704        zwj: 0x200D,
705        lrm: 0x200E,
706        rlm: 0x200F,
707        ndash: 0x2013,
708        mdash: 0x2014,
709        lsquo: 0x2018,
710        rsquo: 0x2019,
711        sbquo: 0x201A,
712        ldquo: 0x201C,
713        rdquo: 0x201D,
714        bdquo: 0x201E,
715        dagger: 0x2020,
716        Dagger: 0x2021,
717        bull: 0x2022,
718        hellip: 0x2026,
719        permil: 0x2030,
720        prime: 0x2032,
721        Prime: 0x2033,
722        lsaquo: 0x2039,
723        rsaquo: 0x203A,
724        oline: 0x203E,
725        frasl: 0x2044,
726        euro: 0x20AC,
727        image: 0x2111,
728        weierp: 0x2118,
729        real: 0x211C,
730        trade: 0x2122,
731        alefsym: 0x2135,
732        larr: 0x2190,
733        uarr: 0x2191,
734        rarr: 0x2192,
735        darr: 0x2193,
736        harr: 0x2194,
737        crarr: 0x21B5,
738        lArr: 0x21D0,
739        uArr: 0x21D1,
740        rArr: 0x21D2,
741        dArr: 0x21D3,
742        hArr: 0x21D4,
743        forall: 0x2200,
744        part: 0x2202,
745        exist: 0x2203,
746        empty: 0x2205,
747        nabla: 0x2207,
748        isin: 0x2208,
749        notin: 0x2209,
750        ni: 0x220B,
751        prod: 0x220F,
752        sum: 0x2211,
753        minus: 0x2212,
754        lowast: 0x2217,
755        radic: 0x221A,
756        prop: 0x221D,
757        infin: 0x221E,
758        ang: 0x2220,
759        and: 0x2227,
760        or: 0x2228,
761        cap: 0x2229,
762        cup: 0x222A,
763        int: 0x222B,
764        there4: 0x2234,
765        sim: 0x223C,
766        cong: 0x2245,
767        asymp: 0x2248,
768        ne: 0x2260,
769        equiv: 0x2261,
770        le: 0x2264,
771        ge: 0x2265,
772        sub: 0x2282,
773        sup: 0x2283,
774        nsub: 0x2284,
775        sube: 0x2286,
776        supe: 0x2287,
777        oplus: 0x2295,
778        otimes: 0x2297,
779        perp: 0x22A5,
780        sdot: 0x22C5,
781        lceil: 0x2308,
782        rceil: 0x2309,
783        lfloor: 0x230A,
784        rfloor: 0x230B,
785        lang: 0x2329,
786        rang: 0x232A,
787        loz: 0x25CA,
788        spades: 0x2660,
789        clubs: 0x2663,
790        hearts: 0x2665,
791        diams: 0x2666
792    }));
793}
794