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

javascript-obfuscator / javascript-obfuscator / 19789202345

29 Nov 2025 08:55PM UTC coverage: 97.319%. Remained the same
19789202345

push

github

sanex3339
Update CHANGELOG.md

1756 of 1876 branches covered (93.6%)

Branch coverage included in aggregate %.

5650 of 5734 relevant lines covered (98.54%)

34059492.4 hits per line

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

97.25
/src/cli/JavaScriptObfuscatorCLI.ts
1
/* eslint-disable max-lines */
2
import * as commander from 'commander';
6✔
3
import * as path from 'path';
6✔
4

5
import { TInputCLIOptions } from '../types/options/TInputCLIOptions';
6
import { TInputOptions } from '../types/options/TInputOptions';
7

8
import { IFileData } from '../interfaces/cli/IFileData';
9
import { IInitializable } from '../interfaces/IInitializable';
10
import { IObfuscationResult } from '../interfaces/source-code/IObfuscationResult';
11

12
import { initializable } from '../decorators/Initializable';
6✔
13

14
import { IdentifierNamesGenerator } from '../enums/generators/identifier-names-generators/IdentifierNamesGenerator';
6✔
15
import { LoggingPrefix } from '../enums/logger/LoggingPrefix';
6✔
16
import { ObfuscationTarget } from '../enums/ObfuscationTarget';
6✔
17
import { OptionsPreset } from '../enums/options/presets/OptionsPreset';
6✔
18
import { RenamePropertiesMode } from '../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
6✔
19
import { SourceMapMode } from '../enums/source-map/SourceMapMode';
6✔
20
import { SourceMapSourcesMode } from '../enums/source-map/SourceMapSourcesMode';
6✔
21
import { StringArrayEncoding } from '../enums/node-transformers/string-array-transformers/StringArrayEncoding';
6✔
22
import { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
6✔
23
import { StringArrayWrappersType } from '../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
6✔
24

25
import { DEFAULT_PRESET } from '../options/presets/Default';
6✔
26

27
import { ArraySanitizer } from './sanitizers/ArraySanitizer';
6✔
28
import { BooleanSanitizer } from './sanitizers/BooleanSanitizer';
6✔
29

30
import { CLIUtils } from './utils/CLIUtils';
6✔
31
import { IdentifierNamesCacheFileUtils } from './utils/IdentifierNamesCacheFileUtils';
6✔
32
import { JavaScriptObfuscator } from '../JavaScriptObfuscatorFacade';
6✔
33
import { Logger } from '../logger/Logger';
6✔
34
import { ObfuscatedCodeFileUtils } from './utils/ObfuscatedCodeFileUtils';
6✔
35
import { SourceCodeFileUtils } from './utils/SourceCodeFileUtils';
6✔
36
import { Utils } from '../utils/Utils';
6✔
37

38
export class JavaScriptObfuscatorCLI implements IInitializable {
6✔
39
    /**
40
     * @type {string[]}
41
     */
42
    public static readonly availableInputExtensions: string[] = [
6✔
43
        '.js',
44
        '.mjs',
45
        '.cjs'
46
    ];
47

48
    /**
49
     * @type {BufferEncoding}
50
     */
51
    public static readonly encoding: BufferEncoding = 'utf8';
6✔
52

53
    /**
54
     * @type {string}
55
     */
56
    public static readonly obfuscatedFilePrefix: string = '-obfuscated';
6✔
57

58
    /**
59
     * @type {commander.Command}
60
     */
61
    @initializable()
62
    private commands!: commander.Command;
6✔
63

64
    /**
65
     * @type {IdentifierNamesCacheFileUtils}
66
     */
67
    @initializable()
68
    private identifierNamesCacheFileUtils!: IdentifierNamesCacheFileUtils;
6✔
69

70
    /**
71
     * @type {TInputCLIOptions}
72
     */
73
    @initializable()
74
    private inputCLIOptions!: TInputCLIOptions;
6✔
75

76
    /**
77
     * @type {string}
78
     */
79
    @initializable()
80
    private inputPath!: string;
6✔
81

82
    /**
83
     * @type {SourceCodeFileUtils}
84
     */
85
    @initializable()
86
    private sourceCodeFileUtils!: SourceCodeFileUtils;
6✔
87

88
    /**
89
     * @type {ObfuscatedCodeFileUtils}
90
     */
91
    @initializable()
92
    private obfuscatedCodeFileUtils!: ObfuscatedCodeFileUtils;
6✔
93

94
    /**
95
     * @type {string[]}
96
     */
97
    private readonly arguments: string[];
98

99
    /**
100
     * @type {string[]}
101
     */
102
    private readonly rawArguments: string[];
103

104
    /**
105
     * @param {string[]} argv
106
     */
107
    public constructor (argv: string[]) {
108
        this.rawArguments = argv;
174✔
109
        this.arguments = argv.slice(2);
174✔
110
    }
111

112
    /**
113
     * @param {TInputCLIOptions} inputOptions
114
     * @returns {TInputOptions}
115
     */
116
    private static buildOptions (inputOptions: TInputCLIOptions): TInputOptions {
117
        const inputCLIOptions: TInputOptions = JavaScriptObfuscatorCLI.filterOptions(inputOptions);
174✔
118
        const configFilePath: string | undefined = inputOptions.config;
174✔
119
        const configFileLocation: string = configFilePath ? path.resolve(configFilePath, '.') : '';
174✔
120
        const configFileOptions: TInputOptions = configFileLocation ? CLIUtils.getUserConfig(configFileLocation) : {};
174✔
121

122
        return {
174✔
123
            ...DEFAULT_PRESET,
124
            ...configFileOptions,
125
            ...inputCLIOptions
126
        };
127
    }
128

129
    /**
130
     * @param {TObject} options
131
     * @returns {TInputOptions}
132
     */
133
    private static filterOptions (options: TInputCLIOptions): TInputOptions {
134
        const filteredOptions: TInputOptions = {};
174✔
135

136
        Object
174✔
137
            .keys(options)
138
            .forEach((option: keyof TInputCLIOptions) => {
139
                if (options[option] === undefined) {
402!
140
                    return;
×
141
                }
142

143
                filteredOptions[option] = options[option];
402✔
144
            });
145

146
        return filteredOptions;
174✔
147
    }
148

149
    public initialize (): void {
150
        this.commands = new commander.Command();
174✔
151

152
        this.configureCommands();
174✔
153
        this.configureHelp();
174✔
154

155
        this.inputPath = path.normalize(this.commands.args[0] || '');
174✔
156
        this.inputCLIOptions = JavaScriptObfuscatorCLI.buildOptions(this.commands.opts());
174✔
157
        this.sourceCodeFileUtils = new SourceCodeFileUtils(
174✔
158
            this.inputPath,
159
            this.inputCLIOptions
160
        );
161
        this.obfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
174✔
162
            this.inputPath,
163
            this.inputCLIOptions
164
        );
165
        this.identifierNamesCacheFileUtils = new IdentifierNamesCacheFileUtils(this.inputCLIOptions.identifierNamesCachePath);
174✔
166
    }
167

168
    public run (): void {
169
        const canShowHelp: boolean = !this.arguments.length || this.arguments.includes('--help');
174✔
170

171
        if (canShowHelp) {
174✔
172
            this.commands.outputHelp();
24✔
173

174
            return;
24✔
175
        }
176

177
        const sourceCodeData: IFileData[] = this.sourceCodeFileUtils.readSourceCode();
150✔
178

179
        this.processSourceCodeData(sourceCodeData);
132✔
180
    }
181

182
    private configureCommands (): void {
183
        this.commands
174✔
184
            .usage('<inputPath> [options]')
185
            .version(
186
                Utils.buildVersionMessage(process.env.VERSION, process.env.BUILD_TIMESTAMP),
187
                '-v, --version'
188
            )
189
            .option(
190
                '-o, --output <path>',
191
                'Output path for obfuscated code'
192
            )
193
            .option(
194
                '--compact <boolean>',
195
                'Disable one line output code compacting',
196
                BooleanSanitizer
197
            )
198
            .option(
199
                '--config <boolean>',
200
                'Name of js / json config file'
201
            )
202
            .option(
203
                '--control-flow-flattening <boolean>',
204
                'Enables control flow flattening',
205
                BooleanSanitizer
206
            )
207
            .option(
208
                '--control-flow-flattening-threshold <number>',
209
                'The probability that the control flow flattening transformation will be applied to the node',
210
                parseFloat
211
            )
212
            .option(
213
                '--dead-code-injection <boolean>',
214
                'Enables dead code injection',
215
                BooleanSanitizer
216
            )
217
            .option(
218
                '--dead-code-injection-threshold <number>',
219
                'The probability that the dead code injection transformation will be applied to the node',
220
                parseFloat
221
            )
222
            .option(
223
                '--debug-protection <boolean>',
224
                'Disable browser Debug panel (can cause DevTools enabled browser freeze)',
225
                BooleanSanitizer
226
            )
227
            .option(
228
                '--debug-protection-interval <number>',
229
                'Sets interval in milliseconds for debug protection so it is working even after page was loaded (can cause DevTools enabled browser freeze)',
230
                parseInt
231
            )
232
            .option(
233
                '--disable-console-output <boolean>',
234
                'Allow console.log, console.info, console.error and console.warn messages output into browser console',
235
                BooleanSanitizer
236
            )
237
            .option(
238
                '--domain-lock <list> (comma separated, without whitespaces)',
239
                'Allows to run the obfuscated source code only on specific domains and/or sub-domains (comma separated)',
240
                ArraySanitizer
241
            )
242
            .option(
243
                '--domain-lock-redirect-url <string>',
244
                'Allows the browser to be redirected to a passed URL if the source code isn\'t run on the domains specified by --domain-lock',
245
            )
246
            .option(
247
                '--exclude <list> (comma separated, without whitespaces)',
248
                'A filename or glob which indicates files to exclude from obfuscation',
249
                ArraySanitizer
250
            )
251
            .option(
252
                '--force-transform-strings <list> (comma separated, without whitespaces)',
253
                'Enables force transformation of string literals, which being matched by passed RegExp patterns (comma separated)',
254
                ArraySanitizer
255
            )
256
            .option(
257
                '--identifier-names-cache-path <string>',
258
                'Sets path for identifier names cache'
259
            )
260
            .option(
261
                '--identifier-names-generator <string>',
262
                'Sets identifier names generator. ' +
263
                `Values: ${CLIUtils.stringifyOptionAvailableValues(IdentifierNamesGenerator)}. ` +
264
                `Default: ${IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator}`
265
            )
266
            .option(
267
                '--identifiers-prefix <string>',
268
                'Sets prefix for all global identifiers'
269
            )
270
            .option(
271
                '--identifiers-dictionary <list> (comma separated, without whitespaces)',
272
                'Identifiers dictionary (comma separated) for `--identifier-names-generator dictionary` option',
273
                ArraySanitizer
274
            )
275
            .option(
276
                '--ignore-imports <boolean>', 'Prevents obfuscation of `require` and `dynamic` imports',
277
                BooleanSanitizer
278
            )
279
            .option(
280
                '--log <boolean>', 'Enables logging of the information to the console',
281
                BooleanSanitizer
282
            )
283
            .option(
284
                '--numbers-to-expressions <boolean>', 'Enables numbers conversion to expressions',
285
                BooleanSanitizer
286
            )
287
            .option(
288
                '--options-preset <string>',
289
                'Allows to set options preset. ' +
290
                `Values: ${CLIUtils.stringifyOptionAvailableValues(OptionsPreset)}. ` +
291
                `Default: ${OptionsPreset.Default}`
292
            )
293
            .option(
294
                '--reserved-names <list> (comma separated, without whitespaces)',
295
                'Disables obfuscation and generation of identifiers, which being matched by passed RegExp patterns (comma separated)',
296
                ArraySanitizer
297
            )
298
            .option(
299
                '--reserved-strings <list> (comma separated, without whitespaces)',
300
                'Disables transformation of string literals, which being matched by passed RegExp patterns (comma separated)',
301
                ArraySanitizer
302
            )
303
            .option(
304
                '--rename-globals <boolean>', 'Allows to enable obfuscation of global variable and function names with declaration',
305
                BooleanSanitizer
306
            )
307
            .option(
308
                '--rename-properties <boolean>', 'UNSAFE: Enables renaming of property names. This probably MAY break your code',
309
                BooleanSanitizer
310
            )
311
            .option(
312
                '--rename-properties-mode <boolean>',
313
                'Specify `--rename-properties` option mode. ' +
314
                `Values: ${CLIUtils.stringifyOptionAvailableValues(RenamePropertiesMode)}. ` +
315
                `Default: ${RenamePropertiesMode.Safe}`
316
            )
317
            .option(
318
                '--seed <string|number>',
319
                'Sets seed for random generator. This is useful for creating repeatable results.',
320
                parseFloat
321
            )
322
            .option(
323
                '--self-defending <boolean>',
324
                'Disables self-defending for obfuscated code',
325
                BooleanSanitizer
326
            )
327
            .option(
328
                '--simplify <boolean>', 'Enables additional code obfuscation through simplification',
329
                BooleanSanitizer
330
            )
331
            .option(
332
                '--source-map <boolean>',
333
                'Enables source map generation',
334
                BooleanSanitizer
335
            )
336
            .option(
337
                '--source-map-base-url <string>',
338
                'Sets base url to the source map import url when `--source-map-mode=separate`'
339
            )
340
            .option(
341
                '--source-map-file-name <string>',
342
                'Sets file name for output source map when `--source-map-mode=separate`'
343
            )
344
            .option(
345
                '--source-map-mode <string>',
346
                'Specify source map output mode. ' +
347
                `Values: ${CLIUtils.stringifyOptionAvailableValues(SourceMapMode)}. ` +
348
                `Default: ${SourceMapMode.Separate}`
349
            )
350
            .option(
351
                '--source-map-sources-mode <string>',
352
                'Specify source map sources mode. ' +
353
                `Values: ${CLIUtils.stringifyOptionAvailableValues(SourceMapSourcesMode)}. ` +
354
                `Default: ${SourceMapSourcesMode.SourcesContent}`
355
            )
356
            .option(
357
                '--split-strings <boolean>',
358
                'Splits literal strings into chunks with length of `splitStringsChunkLength` option value',
359
                BooleanSanitizer
360
            )
361
            .option(
362
                '--split-strings-chunk-length <number>',
363
                'Sets chunk length of `splitStrings` option',
364
                parseFloat
365
            )
366
            .option(
367
                '--string-array <boolean>',
368
                'Enables gathering of all literal strings into an array and replacing every literal string with an array call',
369
                BooleanSanitizer
370
            )
371
            .option(
372
                '--string-array-calls-transform <boolean>',
373
                'Enables the transformation of calls to the string array',
374
                BooleanSanitizer
375
            )
376
            .option(
377
                '--string-array-calls-transform-threshold <number>',
378
                'The probability that that calls to the string array will be transformed',
379
                parseFloat
380
            )
381
            .option(
382
                '--string-array-encoding <list> (comma separated, without whitespaces)',
383
                'Encodes each string in strings array using base64 or rc4 (this option can slow down your code speed). ' +
384
                `Values: ${CLIUtils.stringifyOptionAvailableValues(StringArrayEncoding)}. ` +
385
                `Default: ${StringArrayEncoding.None}`,
386
                ArraySanitizer
387
            )
388
            .option(
389
                '--string-array-indexes-type <list> (comma separated, without whitespaces)',
390
                'Encodes each string in strings array using base64 or rc4 (this option can slow down your code speed). ' +
391
                `Values: ${CLIUtils.stringifyOptionAvailableValues(StringArrayIndexesType)}. ` +
392
                `Default: ${StringArrayIndexesType.HexadecimalNumber}`,
393
                ArraySanitizer
394
            )
395
            .option(
396
                '--string-array-index-shift <boolean>',
397
                'Enables additional index shift for all string array calls',
398
                BooleanSanitizer
399
            )
400
            .option(
401
                '--string-array-rotate <boolean>', 'Enable rotation of string array values during obfuscation',
402
                BooleanSanitizer
403
            )
404
            .option(
405
                '--string-array-shuffle <boolean>', 'Randomly shuffles string array items',
406
                BooleanSanitizer
407
            )
408
            .option(
409
                '--string-array-wrappers-count <number>',
410
                'Sets the count of wrappers for the string array inside each root or function scope',
411
                parseInt
412
            )
413
            .option(
414
                '--string-array-wrappers-chained-calls <boolean>',
415
                'Enables the chained calls between string array wrappers',
416
                BooleanSanitizer
417
            )
418
            .option(
419
                '--string-array-wrappers-parameters-max-count <number>',
420
                'Allows to control the maximum number of string array wrappers parameters',
421
                parseInt
422
            )
423
            .option(
424
                '--string-array-wrappers-type <string>',
425
                'Allows to select a type of the wrappers that are appending by the `--string-array-wrappers-count` option. ' +
426
                `Values: ${CLIUtils.stringifyOptionAvailableValues(StringArrayWrappersType)}. ` +
427
                `Default: ${StringArrayWrappersType.Variable}`
428
            )
429
            .option(
430
                '--string-array-threshold <number>',
431
                'The probability that the literal string will be inserted into stringArray (Default: 0.8, Min: 0, Max: 1)',
432
                parseFloat
433
            )
434
            .option(
435
                '--target <string>',
436
                'Allows to set target environment for obfuscated code. ' +
437
                `Values: ${CLIUtils.stringifyOptionAvailableValues(ObfuscationTarget)}. ` +
438
                `Default: ${ObfuscationTarget.Browser}`
439
            )
440
            .option(
441
                '--transform-object-keys <boolean>',
442
                'Enables transformation of object keys',
443
                BooleanSanitizer
444
            )
445
            .option(
446
                '--unicode-escape-sequence <boolean>',
447
                'Allows to enable/disable string conversion to unicode escape sequence',
448
                BooleanSanitizer
449
            )
450
            .parse(this.rawArguments);
451
    }
452

453
    private configureHelp (): void {
454
        this.commands.on('--help', () => {
174✔
455
            console.log('  Examples:\n');
24✔
456
            console.log('    %> javascript-obfuscator input_file_name.js --compact true --self-defending false');
24✔
457
            console.log('    %> javascript-obfuscator input_file_name.js --output output_file_name.js --compact true --self-defending false');
24✔
458
            console.log('    %> javascript-obfuscator input_directory_name --compact true --self-defending false');
24✔
459
            console.log('');
24✔
460
        });
461
    }
462

463
    /**
464
     * @param {IFileData[]} sourceCodeData
465
     */
466
    private processSourceCodeData (sourceCodeData: IFileData[]): void {
467
        sourceCodeData.forEach(({ filePath, content }: IFileData, index: number) => {
132✔
468
            const outputCodePath: string = this.obfuscatedCodeFileUtils.getOutputCodePath(filePath);
162✔
469

470
            try {
162✔
471
                Logger.log(
162✔
472
                    Logger.colorInfo,
473
                    LoggingPrefix.CLI,
474
                    `Obfuscating file: ${filePath}...`
475
                );
476

477
                this.processSourceCode(content, filePath, outputCodePath, index);
162✔
478
            } catch (error) {
479
                Logger.log(
6✔
480
                    Logger.colorInfo,
481
                    LoggingPrefix.CLI,
482
                    `Error in file: ${filePath}...`
483
                );
484

485
                throw error;
6✔
486
            }
487
        });
488
    }
489

490
    /**
491
     * @param {string} sourceCode
492
     * @param {string} inputCodePath
493
     * @param {string} outputCodePath
494
     * @param {number | null} sourceCodeIndex
495
     */
496
    private processSourceCode (
497
        sourceCode: string,
498
        inputCodePath: string,
499
        outputCodePath: string,
500
        sourceCodeIndex: number | null
501
    ): void {
502
        const options: TInputOptions = {
162✔
503
            ...this.inputCLIOptions,
504
            identifierNamesCache: this.identifierNamesCacheFileUtils.readFile(),
505
            inputFileName: path.basename(inputCodePath),
506
            ...sourceCodeIndex !== null && {
324✔
507
                identifiersPrefix: Utils.getIdentifiersPrefixForMultipleSources(
508
                    this.inputCLIOptions.identifiersPrefix,
509
                    sourceCodeIndex
510
                )
511
            }
512
        };
513

514
        if (options.sourceMap) {
162✔
515
            this.processSourceCodeWithSourceMap(sourceCode, outputCodePath, options);
60✔
516
        } else {
517
            this.processSourceCodeWithoutSourceMap(sourceCode, outputCodePath, options);
102✔
518
        }
519
    }
520

521
    /**
522
     * @param {string} sourceCode
523
     * @param {string} outputCodePath
524
     * @param {TInputOptions} options
525
     */
526
    private processSourceCodeWithoutSourceMap (
527
        sourceCode: string,
528
        outputCodePath: string,
529
        options: TInputOptions
530
    ): void {
531
        const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(sourceCode, options);
102✔
532

533
        this.obfuscatedCodeFileUtils.writeFile(outputCodePath, obfuscationResult.getObfuscatedCode());
96✔
534
        this.identifierNamesCacheFileUtils.writeFile(obfuscationResult.getIdentifierNamesCache());
96✔
535
    }
536

537
    /**
538
     * @param {string} sourceCode
539
     * @param {string} outputCodePath
540
     * @param {TInputOptions} options
541
     */
542
    private processSourceCodeWithSourceMap (
543
        sourceCode: string,
544
        outputCodePath: string,
545
        options: TInputOptions
546
    ): void {
547
        const outputSourceMapPath: string = this.obfuscatedCodeFileUtils.getOutputSourceMapPath(
60✔
548
            outputCodePath,
549
            options.sourceMapFileName ?? ''
180!
550
        );
551

552
        options = {
60✔
553
            ...options,
554
            sourceMapFileName: path.basename(outputSourceMapPath)
555
        };
556

557
        const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(sourceCode, options);
60✔
558

559
        this.obfuscatedCodeFileUtils.writeFile(outputCodePath, obfuscationResult.getObfuscatedCode());
60✔
560
        this.identifierNamesCacheFileUtils.writeFile(obfuscationResult.getIdentifierNamesCache());
60✔
561

562
        if (options.sourceMapMode === SourceMapMode.Separate && obfuscationResult.getSourceMap()) {
60✔
563
            this.obfuscatedCodeFileUtils.writeFile(outputSourceMapPath, obfuscationResult.getSourceMap());
42✔
564
        }
565
    }
566
}
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