• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*@internal*/
2namespace ts {
3    export function transformNodeModule(context: TransformationContext) {
4        const previousOnSubstituteNode = context.onSubstituteNode;
5        const previousOnEmitNode = context.onEmitNode;
6
7        const esmTransform = transformECMAScriptModule(context);
8
9        const esmOnSubstituteNode = context.onSubstituteNode;
10        const esmOnEmitNode = context.onEmitNode;
11
12        context.onSubstituteNode = previousOnSubstituteNode;
13        context.onEmitNode = previousOnEmitNode;
14
15        const cjsTransform = transformModule(context);
16
17        const cjsOnSubstituteNode = context.onSubstituteNode;
18        const cjsOnEmitNode = context.onEmitNode;
19
20        context.onSubstituteNode = onSubstituteNode;
21        context.onEmitNode = onEmitNode;
22        context.enableSubstitution(SyntaxKind.SourceFile);
23        context.enableEmitNotification(SyntaxKind.SourceFile);
24
25        let currentSourceFile: SourceFile | undefined;
26        return transformSourceFileOrBundle;
27
28        function onSubstituteNode(hint: EmitHint, node: Node) {
29            if (isSourceFile(node)) {
30                currentSourceFile = node;
31                // Neither component transform wants substitution notifications for `SourceFile`s, and, in fact, relies on
32                // the source file emit notification to setup scope variables for substitutions (so we _cannot_ call their substitute
33                // functions on source files safely, as that context only gets setup in a later pipeline phase!)
34                return previousOnSubstituteNode(hint, node);
35            }
36            else {
37                if (!currentSourceFile) {
38                    return previousOnSubstituteNode(hint, node);
39                }
40                if (currentSourceFile.impliedNodeFormat === ModuleKind.ESNext) {
41                    return esmOnSubstituteNode(hint, node);
42                }
43                return cjsOnSubstituteNode(hint, node);
44            }
45        }
46
47        function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
48            if (isSourceFile(node)) {
49                currentSourceFile = node;
50            }
51            if (!currentSourceFile) {
52                return previousOnEmitNode(hint, node, emitCallback);
53            }
54            if (currentSourceFile.impliedNodeFormat === ModuleKind.ESNext) {
55                return esmOnEmitNode(hint, node, emitCallback);
56            }
57            return cjsOnEmitNode(hint, node, emitCallback);
58        }
59
60        function getModuleTransformForFile(file: SourceFile): (typeof esmTransform) {
61            return file.impliedNodeFormat === ModuleKind.ESNext ? esmTransform : cjsTransform;
62        }
63
64        function transformSourceFile(node: SourceFile) {
65            if (node.isDeclarationFile) {
66                return node;
67            }
68
69            currentSourceFile = node;
70            const result = getModuleTransformForFile(node)(node);
71            currentSourceFile = undefined;
72            Debug.assert(isSourceFile(result));
73            return result;
74        }
75
76        function transformSourceFileOrBundle(node: SourceFile | Bundle) {
77            return node.kind === SyntaxKind.SourceFile ? transformSourceFile(node) : transformBundle(node);
78        }
79
80        function transformBundle(node: Bundle) {
81            return context.factory.createBundle(map(node.sourceFiles, transformSourceFile), node.prepends);
82        }
83    }
84}
85