• 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

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[] = ['.js', '.mjs', '.cjs'];
6✔
43

44
    /**
45
     * @type {BufferEncoding}
46
     */
47
    public static readonly encoding: BufferEncoding = 'utf8';
6✔
48

49
    /**
50
     * @type {string}
51
     */
52
    public static readonly obfuscatedFilePrefix: string = '-obfuscated';
6✔
53

54
    /**
55
     * @type {commander.Command}
56
     */
57
    @initializable()
58
    private commands!: commander.Command;
6✔
59

60
    /**
61
     * @type {IdentifierNamesCacheFileUtils}
62
     */
63
    @initializable()
64
    private identifierNamesCacheFileUtils!: IdentifierNamesCacheFileUtils;
6✔
65

66
    /**
67
     * @type {TInputCLIOptions}
68
     */
69
    @initializable()
70
    private inputCLIOptions!: TInputCLIOptions;
6✔
71

72
    /**
73
     * @type {string}
74
     */
75
    @initializable()
76
    private inputPath!: string;
6✔
77

78
    /**
79
     * @type {SourceCodeFileUtils}
80
     */
81
    @initializable()
82
    private sourceCodeFileUtils!: SourceCodeFileUtils;
6✔
83

84
    /**
85
     * @type {ObfuscatedCodeFileUtils}
86
     */
87
    @initializable()
88
    private obfuscatedCodeFileUtils!: ObfuscatedCodeFileUtils;
6✔
89

90
    /**
91
     * @type {string[]}
92
     */
93
    private readonly arguments: string[];
94

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

100
    /**
101
     * @param {string[]} argv
102
     */
103
    public constructor(argv: string[]) {
104
        this.rawArguments = argv;
174✔
105
        this.arguments = argv.slice(2);
174✔
106
    }
107

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

118
        return {
174✔
119
            ...DEFAULT_PRESET,
120
            ...configFileOptions,
121
            ...inputCLIOptions
122
        };
123
    }
124

125
    /**
126
     * @param {TObject} options
127
     * @returns {TInputOptions}
128
     */
129
    private static filterOptions(options: TInputCLIOptions): TInputOptions {
130
        const filteredOptions: TInputOptions = {};
174✔
131

132
        Object.keys(options).forEach((option: keyof TInputCLIOptions) => {
174✔
133
            if (options[option] === undefined) {
402!
134
                return;
×
135
            }
136

137
            filteredOptions[option] = options[option];
402✔
138
        });
139

140
        return filteredOptions;
174✔
141
    }
142

143
    public initialize(): void {
144
        this.commands = new commander.Command();
174✔
145

146
        this.configureCommands();
174✔
147
        this.configureHelp();
174✔
148

149
        this.inputPath = path.normalize(this.commands.args[0] || '');
174✔
150
        this.inputCLIOptions = JavaScriptObfuscatorCLI.buildOptions(this.commands.opts());
174✔
151
        this.sourceCodeFileUtils = new SourceCodeFileUtils(this.inputPath, this.inputCLIOptions);
174✔
152
        this.obfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(this.inputPath, this.inputCLIOptions);
174✔
153
        this.identifierNamesCacheFileUtils = new IdentifierNamesCacheFileUtils(
174✔
154
            this.inputCLIOptions.identifierNamesCachePath
155
        );
156
    }
157

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

161
        if (canShowHelp) {
174✔
162
            this.commands.outputHelp();
24✔
163

164
            return;
24✔
165
        }
166

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

169
        this.processSourceCodeData(sourceCodeData);
132✔
170
    }
171

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

396
    private configureHelp(): void {
397
        this.commands.on('--help', () => {
174✔
398
            console.log('  Examples:\n');
24✔
399
            console.log('    %> javascript-obfuscator input_file_name.js --compact true --self-defending false');
24✔
400
            console.log(
24✔
401
                '    %> javascript-obfuscator input_file_name.js --output output_file_name.js --compact true --self-defending false'
402
            );
403
            console.log('    %> javascript-obfuscator input_directory_name --compact true --self-defending false');
24✔
404
            console.log('');
24✔
405
        });
406
    }
407

408
    /**
409
     * @param {IFileData[]} sourceCodeData
410
     */
411
    private processSourceCodeData(sourceCodeData: IFileData[]): void {
412
        sourceCodeData.forEach(({ filePath, content }: IFileData, index: number) => {
132✔
413
            const outputCodePath: string = this.obfuscatedCodeFileUtils.getOutputCodePath(filePath);
162✔
414

415
            try {
162✔
416
                Logger.log(Logger.colorInfo, LoggingPrefix.CLI, `Obfuscating file: ${filePath}...`);
162✔
417

418
                this.processSourceCode(content, filePath, outputCodePath, index);
162✔
419
            } catch (error) {
420
                Logger.log(Logger.colorInfo, LoggingPrefix.CLI, `Error in file: ${filePath}...`);
6✔
421

422
                throw error;
6✔
423
            }
424
        });
425
    }
426

427
    /**
428
     * @param {string} sourceCode
429
     * @param {string} inputCodePath
430
     * @param {string} outputCodePath
431
     * @param {number | null} sourceCodeIndex
432
     */
433
    private processSourceCode(
434
        sourceCode: string,
435
        inputCodePath: string,
436
        outputCodePath: string,
437
        sourceCodeIndex: number | null
438
    ): void {
439
        const options: TInputOptions = {
162✔
440
            ...this.inputCLIOptions,
441
            identifierNamesCache: this.identifierNamesCacheFileUtils.readFile(),
442
            inputFileName: path.basename(inputCodePath),
443
            ...(sourceCodeIndex !== null && {
324✔
444
                identifiersPrefix: Utils.getIdentifiersPrefixForMultipleSources(
445
                    this.inputCLIOptions.identifiersPrefix,
446
                    sourceCodeIndex
447
                )
448
            })
449
        };
450

451
        if (options.sourceMap) {
162✔
452
            this.processSourceCodeWithSourceMap(sourceCode, outputCodePath, options);
60✔
453
        } else {
454
            this.processSourceCodeWithoutSourceMap(sourceCode, outputCodePath, options);
102✔
455
        }
456
    }
457

458
    /**
459
     * @param {string} sourceCode
460
     * @param {string} outputCodePath
461
     * @param {TInputOptions} options
462
     */
463
    private processSourceCodeWithoutSourceMap(
464
        sourceCode: string,
465
        outputCodePath: string,
466
        options: TInputOptions
467
    ): void {
468
        const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(sourceCode, options);
102✔
469

470
        this.obfuscatedCodeFileUtils.writeFile(outputCodePath, obfuscationResult.getObfuscatedCode());
96✔
471
        this.identifierNamesCacheFileUtils.writeFile(obfuscationResult.getIdentifierNamesCache());
96✔
472
    }
473

474
    /**
475
     * @param {string} sourceCode
476
     * @param {string} outputCodePath
477
     * @param {TInputOptions} options
478
     */
479
    private processSourceCodeWithSourceMap(sourceCode: string, outputCodePath: string, options: TInputOptions): void {
480
        const outputSourceMapPath: string = this.obfuscatedCodeFileUtils.getOutputSourceMapPath(
60✔
481
            outputCodePath,
482
            options.sourceMapFileName ?? ''
180!
483
        );
484

485
        options = {
60✔
486
            ...options,
487
            sourceMapFileName: path.basename(outputSourceMapPath)
488
        };
489

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

492
        this.obfuscatedCodeFileUtils.writeFile(outputCodePath, obfuscationResult.getObfuscatedCode());
60✔
493
        this.identifierNamesCacheFileUtils.writeFile(obfuscationResult.getIdentifierNamesCache());
60✔
494

495
        if (options.sourceMapMode === SourceMapMode.Separate && obfuscationResult.getSourceMap()) {
60✔
496
            this.obfuscatedCodeFileUtils.writeFile(outputSourceMapPath, obfuscationResult.getSourceMap());
42✔
497
        }
498
    }
499
}
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