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