• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*@internal*/
2namespace ts {
3    /**
4     * Transforms ES5 syntax into ES3 syntax.
5     *
6     * @param context Context and state information for the transformation.
7     */
8    export function transformES5(context: TransformationContext) {
9        const { factory } = context;
10        const compilerOptions = context.getCompilerOptions();
11
12        // enable emit notification only if using --jsx preserve or react-native
13        let previousOnEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
14        let noSubstitution: boolean[];
15        if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) {
16            previousOnEmitNode = context.onEmitNode;
17            context.onEmitNode = onEmitNode;
18            context.enableEmitNotification(SyntaxKind.JsxOpeningElement);
19            context.enableEmitNotification(SyntaxKind.JsxClosingElement);
20            context.enableEmitNotification(SyntaxKind.JsxSelfClosingElement);
21            noSubstitution = [];
22        }
23
24        const previousOnSubstituteNode = context.onSubstituteNode;
25        context.onSubstituteNode = onSubstituteNode;
26        context.enableSubstitution(SyntaxKind.PropertyAccessExpression);
27        context.enableSubstitution(SyntaxKind.PropertyAssignment);
28        return chainBundle(context, transformSourceFile);
29
30        /**
31         * Transforms an ES5 source file to ES3.
32         *
33         * @param node A SourceFile
34         */
35        function transformSourceFile(node: SourceFile) {
36            return node;
37        }
38
39        /**
40         * Called by the printer just before a node is printed.
41         *
42         * @param hint A hint as to the intended usage of the node.
43         * @param node The node to emit.
44         * @param emitCallback A callback used to emit the node.
45         */
46        function onEmitNode(hint: EmitHint, node: Node, emitCallback: (emitContext: EmitHint, node: Node) => void) {
47            switch (node.kind) {
48                case SyntaxKind.JsxOpeningElement:
49                case SyntaxKind.JsxClosingElement:
50                case SyntaxKind.JsxSelfClosingElement:
51                    const tagName = (node as JsxOpeningElement | JsxClosingElement | JsxSelfClosingElement).tagName;
52                    noSubstitution[getOriginalNodeId(tagName)] = true;
53                    break;
54            }
55
56            previousOnEmitNode(hint, node, emitCallback);
57        }
58
59        /**
60         * Hooks node substitutions.
61         *
62         * @param hint A hint as to the intended usage of the node.
63         * @param node The node to substitute.
64         */
65        function onSubstituteNode(hint: EmitHint, node: Node) {
66            if (node.id && noSubstitution && noSubstitution[node.id]) {
67                return previousOnSubstituteNode(hint, node);
68            }
69
70            node = previousOnSubstituteNode(hint, node);
71            if (isPropertyAccessExpression(node)) {
72                return substitutePropertyAccessExpression(node);
73            }
74            else if (isPropertyAssignment(node)) {
75                return substitutePropertyAssignment(node);
76            }
77            return node;
78        }
79
80        /**
81         * Substitutes a PropertyAccessExpression whose name is a reserved word.
82         *
83         * @param node A PropertyAccessExpression
84         */
85        function substitutePropertyAccessExpression(node: PropertyAccessExpression): Expression {
86            if (isPrivateIdentifier(node.name)) {
87                return node;
88            }
89            const literalName = trySubstituteReservedName(node.name);
90            if (literalName) {
91                return setTextRange(factory.createElementAccessExpression(node.expression, literalName), node);
92            }
93            return node;
94        }
95
96        /**
97         * Substitutes a PropertyAssignment whose name is a reserved word.
98         *
99         * @param node A PropertyAssignment
100         */
101        function substitutePropertyAssignment(node: PropertyAssignment): PropertyAssignment {
102            const literalName = isIdentifier(node.name) && trySubstituteReservedName(node.name);
103            if (literalName) {
104                return factory.updatePropertyAssignment(node, literalName, node.initializer);
105            }
106            return node;
107        }
108
109        /**
110         * If an identifier name is a reserved word, returns a string literal for the name.
111         *
112         * @param name An Identifier
113         */
114        function trySubstituteReservedName(name: Identifier) {
115            const token = name.originalKeywordKind || (nodeIsSynthesized(name) ? stringToToken(idText(name)) : undefined);
116            if (token !== undefined && token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord) {
117                return setTextRange(factory.createStringLiteralFromNode(name), name);
118            }
119            return undefined;
120        }
121    }
122}
123