• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

javascript-obfuscator / javascript-obfuscator / 19907815758

03 Dec 2025 08:27PM UTC coverage: 97.319%. Remained the same
19907815758

push

github

sanex3339
Adjust precommit hook

1770 of 1891 branches covered (93.6%)

Branch coverage included in aggregate %.

5671 of 5755 relevant lines covered (98.54%)

34102965.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.6
/src/node-transformers/initializing-transformers/CommentsTransformer.ts
1
import { inject, injectable } from 'inversify';
6✔
2
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
6✔
3

4
import * as ESTree from 'estree';
5
import * as estraverse from '@javascript-obfuscator/estraverse';
6✔
6

7
import { IOptions } from '../../interfaces/options/IOptions';
8
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
9
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
10

11
import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
6✔
12

13
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
6✔
14
import { ConditionalCommentObfuscatingGuard } from '../preparing-transformers/obfuscating-guards/ConditionalCommentObfuscatingGuard';
6✔
15
import { NodeGuards } from '../../node/NodeGuards';
6✔
16

17
@injectable()
18
export class CommentsTransformer extends AbstractNodeTransformer {
6✔
19
    /**
20
     * @type {string[]}
21
     */
22
    private static readonly preservedWords: string[] = ['@license', '@preserve'];
6✔
23

24
    /**
25
     * @param {IRandomGenerator} randomGenerator
26
     * @param {IOptions} options
27
     */
28
    public constructor(
29
        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
30
        @inject(ServiceIdentifiers.IOptions) options: IOptions
31
    ) {
32
        super(randomGenerator, options);
197,808✔
33
    }
34

35
    /**
36
     * @param {NodeTransformationStage} nodeTransformationStage
37
     * @returns {IVisitor | null}
38
     */
39
    public getVisitor(nodeTransformationStage: NodeTransformationStage): IVisitor | null {
40
        switch (nodeTransformationStage) {
1,810,518✔
41
            case NodeTransformationStage.Initializing:
1,810,518✔
42
                return {
395,616✔
43
                    leave: (node: ESTree.Node): ESTree.Node | undefined => {
44
                        if (NodeGuards.isProgramNode(node)) {
16,439,946✔
45
                            return this.transformNode(node);
197,808✔
46
                        }
47
                    }
48
                };
49

50
            case NodeTransformationStage.Finalizing:
51
                return {
395,352✔
52
                    leave: (node: ESTree.Node): ESTree.Node | undefined => {
53
                        if (NodeGuards.isProgramNode(node)) {
364,377,571✔
54
                            return this.filterCommentsOnFinalizingTraverse(node);
197,676✔
55
                        }
56
                    }
57
                };
58

59
            default:
60
                return null;
1,019,550✔
61
        }
62
    }
63

64
    /**
65
     * Moves comments to their nodes
66
     */
67
    public transformNode(rootNode: ESTree.Program): ESTree.Node {
68
        rootNode = this.filterCommentsOnPrimaryTraverse(rootNode);
197,808✔
69

70
        if (!rootNode.comments?.length) {
197,808✔
71
            return rootNode;
197,634✔
72
        }
73

74
        const comments: ESTree.Comment[] = rootNode.comments.reverse();
174✔
75

76
        if (comments.length === 0) {
174!
77
            return rootNode;
×
78
        }
79

80
        if (!rootNode.body.length) {
174✔
81
            rootNode.leadingComments = comments;
6✔
82

83
            return rootNode;
6✔
84
        }
85

86
        let isFirstNode: boolean = true;
168✔
87

88
        estraverse.traverse(rootNode, {
168✔
89
            enter: (node: ESTree.Node): void => {
90
                if (node === rootNode) {
5,178✔
91
                    return;
168✔
92
                }
93

94
                const commentIdx: number = comments.findIndex(
5,010✔
95
                    (comment: ESTree.Comment) => comment.range && node.range && comment.range[0] < node.range[0]
4,062✔
96
                );
97

98
                if (commentIdx >= 0) {
5,010✔
99
                    (isFirstNode ? rootNode : node).leadingComments = comments
264✔
100
                        .splice(commentIdx, comments.length - commentIdx)
101
                        .reverse();
102
                }
103

104
                isFirstNode = false;
5,010✔
105
            }
106
        });
107

108
        if (comments.length > 0) {
168✔
109
            rootNode.trailingComments = comments.reverse();
12✔
110
        }
111

112
        return rootNode;
168✔
113
    }
114

115
    /**
116
     * Removes all comments from node except comments that contain
117
     * preserved words or `javascript-obfuscator` words
118
     *
119
     * @param {ESTree.Program} rootNode
120
     * @returns {ESTree.Program}
121
     */
122
    private filterCommentsOnPrimaryTraverse(rootNode: ESTree.Program): ESTree.Program {
123
        rootNode.comments = rootNode.comments?.filter((comment: ESTree.Comment) => this.filterComment(comment, true));
197,808✔
124

125
        return rootNode;
197,808✔
126
    }
127

128
    /**
129
     * Removes all comments from node except comments that contain preserved words
130
     *
131
     * @param {ESTree.Program} rootNode
132
     * @returns {ESTree.Program}
133
     */
134
    private filterCommentsOnFinalizingTraverse(rootNode: ESTree.Program): ESTree.Program {
135
        estraverse.replace(rootNode, {
197,676✔
136
            enter: (node: ESTree.Node): ESTree.Node => {
137
                if (node.leadingComments) {
364,452,060✔
138
                    node.leadingComments = node.leadingComments?.filter((comment: ESTree.Comment) =>
270!
139
                        this.filterComment(comment, false)
270✔
140
                    );
141
                }
142

143
                if (node.trailingComments) {
364,452,060✔
144
                    node.trailingComments = node.trailingComments?.filter((comment: ESTree.Comment) =>
12!
145
                        this.filterComment(comment, false)
12✔
146
                    );
147
                }
148

149
                return node;
364,452,060✔
150
            }
151
        });
152

153
        return rootNode;
197,676✔
154
    }
155

156
    /**
157
     * @param {ESTree.Comment} comment
158
     * @param {boolean} keepConditionalComment
159
     * @returns {boolean}
160
     */
161
    private filterComment(comment: ESTree.Comment, keepConditionalComment: boolean): boolean {
162
        if (keepConditionalComment && ConditionalCommentObfuscatingGuard.isConditionalComment(comment)) {
4,164✔
163
            return true;
240✔
164
        }
165

166
        return CommentsTransformer.preservedWords.some((preservedWord: string) =>
3,924✔
167
            comment.value.includes(preservedWord)
7,776✔
168
        );
169
    }
170
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc