Browse Source

Identifier names cache option prototype

sanex 4 years ago
parent
commit
f087100c26

File diff suppressed because it is too large
+ 0 - 0
dist/index.cli.js


File diff suppressed because it is too large
+ 0 - 0
dist/index.js


+ 17 - 2
src/cli/JavaScriptObfuscatorCLI.ts

@@ -27,6 +27,7 @@ import { ArraySanitizer } from './sanitizers/ArraySanitizer';
 import { BooleanSanitizer } from './sanitizers/BooleanSanitizer';
 
 import { CLIUtils } from './utils/CLIUtils';
+import { IdentifierNamesCacheUtils } from './utils/IdentifierNamesCacheUtils';
 import { JavaScriptObfuscator } from '../JavaScriptObfuscatorFacade';
 import { Logger } from '../logger/Logger';
 import { ObfuscatedCodeWriter } from './utils/ObfuscatedCodeWriter';
@@ -57,6 +58,12 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
     @initializable()
     private commands!: commander.CommanderStatic;
 
+    /**
+     * @type {IdentifierNamesCacheUtils}
+     */
+    @initializable()
+    private identifierNamesCacheUtils!: IdentifierNamesCacheUtils;
+
     /**
      * @type {TInputCLIOptions}
      */
@@ -152,6 +159,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
             this.inputPath,
             this.inputCLIOptions
         );
+        this.identifierNamesCacheUtils = new IdentifierNamesCacheUtils(this.inputCLIOptions.identifierNamesCachePath);
     }
 
     public run (): void {
@@ -238,6 +246,10 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 'Enables force transformation of string literals, which being matched by passed RegExp patterns (comma separated)',
                 ArraySanitizer
             )
+            .option(
+                '--identifier-names-cache-path <string>',
+                'Sets path for identifier names cache'
+            )
             .option(
                 '--identifier-names-generator <string>',
                 'Sets identifier names generator. ' +
@@ -466,6 +478,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
     ): void {
         const options: TInputOptions = {
             ...this.inputCLIOptions,
+            identifierNamesCache: this.identifierNamesCacheUtils.read(),
             inputFileName: path.basename(inputCodePath),
             ...sourceCodeIndex !== null && {
                 identifiersPrefix: Utils.getIdentifiersPrefixForMultipleSources(
@@ -492,9 +505,10 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
         outputCodePath: string,
         options: TInputOptions
     ): void {
-        const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(sourceCode, options).getObfuscatedCode();
+        const obfuscatedCode: IObfuscatedCode = JavaScriptObfuscator.obfuscate(sourceCode, options);
 
-        this.obfuscatedCodeWriter.writeFile(outputCodePath, obfuscatedCode);
+        this.obfuscatedCodeWriter.writeFile(outputCodePath, obfuscatedCode.getObfuscatedCode());
+        this.identifierNamesCacheUtils.write(obfuscatedCode.getIdentifierNamesCache());
     }
 
     /**
@@ -520,6 +534,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
         const obfuscatedCode: IObfuscatedCode = JavaScriptObfuscator.obfuscate(sourceCode, options);
 
         this.obfuscatedCodeWriter.writeFile(outputCodePath, obfuscatedCode.getObfuscatedCode());
+        this.identifierNamesCacheUtils.write(obfuscatedCode.getIdentifierNamesCache());
 
         if (options.sourceMapMode === SourceMapMode.Separate && obfuscatedCode.getSourceMap()) {
             this.obfuscatedCodeWriter.writeFile(outputSourceMapPath, obfuscatedCode.getSourceMap());

+ 98 - 0
src/cli/utils/IdentifierNamesCacheUtils.ts

@@ -0,0 +1,98 @@
+import * as fs from 'fs';
+import * as path from 'path';
+
+import { TIdentifierNamesCache } from '../../types/caches/TIdentifierNamesCache';
+
+import { IFileData } from '../../interfaces/cli/IFileData';
+
+import { JavaScriptObfuscatorCLI } from '../JavaScriptObfuscatorCLI';
+
+/**
+ * Utils to work with identifier names cache file
+ */
+export class IdentifierNamesCacheUtils {
+    /**
+     * @type {string}
+     */
+    private static readonly identifierNamesCacheExtension: string = '.json';
+
+    /**
+     * @type {string}
+     */
+    private readonly identifierNamesCachePath: string | undefined;
+
+    /**
+     * @param {string} identifierNamesCachePath
+     */
+    public constructor (identifierNamesCachePath: string | undefined) {
+        this.identifierNamesCachePath = identifierNamesCachePath;
+    }
+
+    /**
+     * @param {string} filePath
+     * @returns {boolean}
+     */
+    private static isValidFilePath (filePath: string): boolean {
+        try {
+            return fs.statSync(filePath).isFile()
+                && path.extname(filePath) === IdentifierNamesCacheUtils.identifierNamesCacheExtension;
+        } catch {
+            return false;
+        }
+    }
+
+    /**
+     * @param {string} filePath
+     * @returns {IFileData}
+     */
+    private static readFile (filePath: string): IFileData {
+        return {
+            filePath: path.normalize(filePath),
+            content: fs.readFileSync(filePath, JavaScriptObfuscatorCLI.encoding)
+        };
+    }
+
+    /**
+     * @returns {TIdentifierNamesCache | null}
+     */
+    public read (): TIdentifierNamesCache | null {
+        if (!this.identifierNamesCachePath) {
+            return null;
+        }
+
+        if (!IdentifierNamesCacheUtils.isValidFilePath(this.identifierNamesCachePath)) {
+            throw new ReferenceError(`Given identifier names cache path must be a valid ${
+                IdentifierNamesCacheUtils.identifierNamesCacheExtension
+            } file path`);
+        }
+
+        const fileData: IFileData = IdentifierNamesCacheUtils.readFile(this.identifierNamesCachePath);
+
+        if (!fileData.content) {
+            // Initial state of identifier names cache file
+            return {};
+        }
+
+        try {
+            // Already written identifier names cache file
+            return JSON.parse(fileData.content);
+        } catch {
+            throw new ReferenceError('Identifier names cache file must contains a json dictionary with identifier names');
+        }
+    }
+
+    /**
+     * @param {TIdentifierNamesCache} identifierNamesCache
+     */
+    public write (identifierNamesCache: TIdentifierNamesCache): void {
+        if (!this.identifierNamesCachePath) {
+            return;
+        }
+
+        const identifierNamesCacheJson: string = JSON.stringify(identifierNamesCache);
+
+        fs.writeFileSync(this.identifierNamesCachePath, identifierNamesCacheJson, {
+            encoding: JavaScriptObfuscatorCLI.encoding
+        });
+    }
+}

+ 1 - 0
src/container/ServiceIdentifiers.ts

@@ -29,6 +29,7 @@ export enum ServiceIdentifiers {
     ICustomCodeHelperFormatter = 'ICustomCodeHelperFormatter',
     ICustomCodeHelperObfuscator = 'ICustomCodeHelperObfuscator',
     IEscapeSequenceEncoder = 'IEscapeSequenceEncoder',
+    IIdentifierNamesCacheStorage = 'IIdentifierNamesCacheStorage',
     IIdentifierNamesGenerator = 'IIdentifierNamesGenerator',
     IIdentifierReplacer = 'IIdentifierReplacer',
     IJavaScriptObfuscator = 'IJavaScriptObfuscator',

+ 6 - 0
src/container/modules/storages/StoragesModule.ts

@@ -4,6 +4,7 @@ import { ServiceIdentifiers } from '../../ServiceIdentifiers';
 import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage';
 import { TCustomCodeHelperGroupStorage } from '../../../types/storages/TCustomCodeHelperGroupStorage';
 
+import { IIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IIdentifierNamesCacheStorage';
 import { ILiteralNodesCacheStorage } from '../../../interfaces/storages/string-array-transformers/ILiteralNodesCacheStorage';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
@@ -14,6 +15,7 @@ import { IVisitedLexicalScopeNodesStackStorage } from '../../../interfaces/stora
 
 import { ControlFlowStorage } from '../../../storages/custom-nodes/ControlFlowStorage';
 import { CustomCodeHelperGroupStorage } from '../../../storages/custom-code-helpers/CustomCodeHelperGroupStorage';
+import { IdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/IdentifierNamesCacheStorage';
 import { LiteralNodesCacheStorage } from '../../../storages/string-array-transformers/LiteralNodesCacheStorage';
 import { StringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrapperLexicalScopeDataStorage';
 import { StringArrayScopeCallsWrapperNamesDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrapperNamesDataStorage';
@@ -26,6 +28,10 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b
         .to(CustomCodeHelperGroupStorage)
         .inSingletonScope();
 
+    bind<IIdentifierNamesCacheStorage>(ServiceIdentifiers.IIdentifierNamesCacheStorage)
+        .to(IdentifierNamesCacheStorage)
+        .inSingletonScope();
+
     bind<ILiteralNodesCacheStorage>(ServiceIdentifiers.ILiteralNodesCacheStorage)
         .to(LiteralNodesCacheStorage)
         .inSingletonScope();

+ 1 - 0
src/interfaces/options/ICLIOptions.ts

@@ -3,6 +3,7 @@ import { IOptions } from './IOptions';
 export interface ICLIOptions extends IOptions {
     readonly config: string;
     readonly exclude: string[];
+    readonly identifierNamesCachePath: string;
     readonly output: string;
     readonly version: string;
 }

+ 2 - 0
src/interfaces/options/IOptions.ts

@@ -1,3 +1,4 @@
+import { TIdentifierNamesCache } from '../../types/caches/TIdentifierNamesCache';
 import { TOptionsPreset } from '../../types/options/TOptionsPreset';
 import { TStringArrayIndexesType } from '../../types/options/TStringArrayIndexesType';
 import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
@@ -20,6 +21,7 @@ export interface IOptions {
     readonly disableConsoleOutput: boolean;
     readonly domainLock: string[];
     readonly forceTransformStrings: string[];
+    readonly identifierNamesCache: TIdentifierNamesCache | null;
     readonly identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>;
     readonly identifiersDictionary: string[];
     readonly identifiersPrefix: string;

+ 6 - 0
src/interfaces/source-code/IObfuscatedCode.ts

@@ -1,6 +1,12 @@
+import { TIdentifierNamesCache } from '../../types/caches/TIdentifierNamesCache';
 import { IInitializable } from '../IInitializable';
 
 export interface IObfuscatedCode extends IInitializable <[string, string]> {
+    /**
+     * @returns {TIdentifierNamesCache}
+     */
+    getIdentifierNamesCache (): TIdentifierNamesCache;
+
     /**
      * @return {string}
      */

+ 7 - 0
src/interfaces/storages/IMapStorage.ts

@@ -1,3 +1,5 @@
+import { TDictionary } from '../../types/TDictionary';
+
 import { IInitializable } from '../IInitializable';
 
 export interface IMapStorage <K, V> extends IInitializable {
@@ -7,6 +9,11 @@ export interface IMapStorage <K, V> extends IInitializable {
      */
     get (key: K): V | undefined;
 
+    /**
+     * @returns {TDictionary<V>}
+     */
+    getAsDictionary (): TDictionary<V>;
+
     /**
      * @param {K} key
      * @returns {V}

+ 4 - 0
src/interfaces/storages/identifier-names-cache/IIdentifierNamesCacheStorage.ts

@@ -0,0 +1,4 @@
+import { IMapStorage } from '../IMapStorage';
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface IIdentifierNamesCacheStorage extends IMapStorage <string, string> {}

+ 22 - 1
src/node-transformers/rename-identifiers-transformers/replacer/IdentifierReplacer.ts

@@ -6,6 +6,7 @@ import * as ESTree from 'estree';
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TNodeWithLexicalScope } from '../../../types/node/TNodeWithLexicalScope';
 
+import { IIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IIdentifierNamesCacheStorage';
 import { IIdentifierNamesGenerator } from '../../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator';
 import { IIdentifierReplacer } from '../../../interfaces/node-transformers/rename-identifiers-transformers/replacer/IIdentifierReplacer';
 import { IOptions } from '../../../interfaces/options/IOptions';
@@ -14,6 +15,11 @@ import { NodeFactory } from '../../../node/NodeFactory';
 
 @injectable()
 export class IdentifierReplacer implements IIdentifierReplacer {
+    /**
+     * @type {IIdentifierNamesCacheStorage}
+     */
+    private readonly identifierNamesCacheStorage: IIdentifierNamesCacheStorage;
+
     /**
      * @type {IIdentifierNamesGenerator}
      */
@@ -31,14 +37,18 @@ export class IdentifierReplacer implements IIdentifierReplacer {
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
+     * @param {IIdentifierNamesCacheStorage} identifierNamesCacheStorage
      * @param {IOptions} options
      */
     public constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
+        @inject(ServiceIdentifiers.IIdentifierNamesCacheStorage)
+            identifierNamesCacheStorage: IIdentifierNamesCacheStorage,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         this.options = options;
+        this.identifierNamesCacheStorage = identifierNamesCacheStorage;
         this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
     }
 
@@ -56,7 +66,14 @@ export class IdentifierReplacer implements IIdentifierReplacer {
             return;
         }
 
-        const newIdentifierName: string = this.identifierNamesGenerator.generateForGlobalScope();
+        const valueFromIdentifierNamesCache: string | null = this.identifierNamesCacheStorage.get(identifierName) ?? null;
+        let newIdentifierName: string;
+
+        if (valueFromIdentifierNamesCache) {
+            newIdentifierName = valueFromIdentifierNamesCache;
+        } else {
+            newIdentifierName = this.identifierNamesGenerator.generateForGlobalScope();
+        }
 
         if (!this.blockScopesMap.has(lexicalScopeNode)) {
             this.blockScopesMap.set(lexicalScopeNode, new Map());
@@ -65,6 +82,10 @@ export class IdentifierReplacer implements IIdentifierReplacer {
         const namesMap: Map<string, string> = <Map<string, string>>this.blockScopesMap.get(lexicalScopeNode);
 
         namesMap.set(identifierName, newIdentifierName);
+
+        if (valueFromIdentifierNamesCache !== newIdentifierName) {
+            this.identifierNamesCacheStorage.set(identifierName, newIdentifierName);
+        }
     }
 
     /**

+ 8 - 0
src/options/Options.ts

@@ -18,6 +18,7 @@ import {
     ValidatorOptions
 } from 'class-validator';
 
+import { TIdentifierNamesCache } from '../types/caches/TIdentifierNamesCache';
 import { TInputOptions } from '../types/options/TInputOptions';
 import { TOptionsPreset } from '../types/options/TOptionsPreset';
 import { TRenamePropertiesMode } from '../types/options/TRenamePropertiesMode';
@@ -45,6 +46,7 @@ import { HIGH_OBFUSCATION_PRESET } from './presets/HighObfuscation';
 
 import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
 import { IsAllowedForObfuscationTargets } from './validators/IsAllowedForObfuscationTargets';
+import { IsPrimitiveDictionary } from './validators/IsPrimitiveDictionary';
 
 @injectable()
 export class Options implements IOptions {
@@ -142,6 +144,12 @@ export class Options implements IOptions {
     })
     public readonly forceTransformStrings!: string[];
 
+    /**
+     * @type {TIdentifierNamesCache}
+     */
+    @IsPrimitiveDictionary('string')
+    public readonly identifierNamesCache!: TIdentifierNamesCache;
+
     /**
      * @type {IdentifierNamesGenerator}
      */

+ 1 - 0
src/options/presets/Default.ts

@@ -22,6 +22,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     domainLock: [],
     exclude: [],
     forceTransformStrings: [],
+    identifierNamesCache: null,
     identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
     identifiersPrefix: '',
     identifiersDictionary: [],

+ 66 - 0
src/options/validators/IsPrimitiveDictionary.ts

@@ -0,0 +1,66 @@
+import equal from 'fast-deep-equal';
+import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+
+import { DEFAULT_PRESET } from '../presets/Default';
+
+/**
+ * @param {"string" | "number"} valuesType
+ * @param {ValidationOptions} validationOptions
+ * @returns {(options: IOptions, propertyName: keyof IOptions) => void}
+ */
+export function IsPrimitiveDictionary (
+    valuesType: 'string' | 'number',
+    validationOptions?: ValidationOptions
+): (options: IOptions, propertyName: keyof IOptions) => void {
+    return (optionsObject: IOptions, propertyName: keyof IOptions): void => {
+        registerDecorator({
+            propertyName,
+            constraints: [valuesType],
+            name: 'IsPrimitiveDictionary',
+            options: validationOptions,
+            target: optionsObject.constructor,
+            validator: {
+                /**
+                 * @param value
+                 * @param {ValidationArguments} validationArguments
+                 * @returns {boolean}
+                 */
+                validate (value: IOptions[keyof IOptions], validationArguments: ValidationArguments): boolean {
+                    const defaultValue: IOptions[keyof IOptions] | undefined = DEFAULT_PRESET[propertyName];
+                    const isDefaultValue: boolean = equal(value, defaultValue);
+
+                    if (isDefaultValue || value === null) {
+                        return true;
+                    }
+
+                    if (typeof value !== 'object') {
+                        return false;
+                    }
+
+                    const objectValues: unknown[] = Object.values<unknown>(value);
+
+                    if (!objectValues.length) {
+                        return true;
+                    }
+
+                    for (const objectValue of objectValues) {
+                        if (typeof objectValue !== 'string') {
+                            return false;
+                        }
+                    }
+
+                    return true;
+                },
+
+                /**
+                 * @returns {string}
+                 */
+                defaultMessage (): string {
+                    return `Passed value must be a dictionary with \`${valuesType}\` values or \`null\` value`;
+                }
+            }
+        });
+    };
+}

+ 24 - 1
src/source-code/ObfuscatedCode.ts

@@ -1,12 +1,15 @@
 import { inject, injectable } from 'inversify';
 import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
 
+import { TIdentifierNamesCache } from '../types/caches/TIdentifierNamesCache';
+
 import { ICryptUtils } from '../interfaces/utils/ICryptUtils';
+import { IIdentifierNamesCacheStorage } from '../interfaces/storages/identifier-names-cache/IIdentifierNamesCacheStorage';
 import { IObfuscatedCode } from '../interfaces/source-code/IObfuscatedCode';
+import { IOptions } from '../interfaces/options/IOptions';
 
 import { initializable } from '../decorators/Initializable';
 import { SourceMapMode } from '../enums/source-map/SourceMapMode';
-import { IOptions } from '../interfaces/options/IOptions';
 
 @injectable()
 export class ObfuscatedCode implements IObfuscatedCode {
@@ -27,16 +30,29 @@ export class ObfuscatedCode implements IObfuscatedCode {
      */
     private readonly cryptUtils: ICryptUtils;
 
+    /**
+     * @type {IIdentifierNamesCacheStorage}
+     */
+    private readonly identifierNamesCacheStorage: IIdentifierNamesCacheStorage;
+
     /**
      * @type {IOptions}
      */
     private readonly options: IOptions;
 
+    /**
+     * @param {ICryptUtils} cryptUtils
+     * @param {IIdentifierNamesCacheStorage} identifierNamesCacheStorage
+     * @param {IOptions} options
+     */
     public constructor (
         @inject(ServiceIdentifiers.ICryptUtils) cryptUtils: ICryptUtils,
+        @inject(ServiceIdentifiers.IIdentifierNamesCacheStorage)
+            identifierNamesCacheStorage: IIdentifierNamesCacheStorage,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         this.cryptUtils = cryptUtils;
+        this.identifierNamesCacheStorage = identifierNamesCacheStorage;
         this.options = options;
     }
 
@@ -49,6 +65,13 @@ export class ObfuscatedCode implements IObfuscatedCode {
         this.sourceMap = sourceMap;
     }
 
+    /**
+     * @returns {string}
+     */
+    public getIdentifierNamesCache (): TIdentifierNamesCache {
+        return this.identifierNamesCacheStorage.getAsDictionary();
+    }
+
     /**
      * @returns {string}
      */

+ 8 - 0
src/storages/MapStorage.ts

@@ -6,6 +6,7 @@ import { IOptions } from '../interfaces/options/IOptions';
 import { IRandomGenerator } from '../interfaces/utils/IRandomGenerator';
 
 import { initializable } from '../decorators/Initializable';
+import { TDictionary } from '../types/TDictionary';
 
 @injectable()
 export abstract class MapStorage <K, V> implements IMapStorage <K, V> {
@@ -57,6 +58,13 @@ export abstract class MapStorage <K, V> implements IMapStorage <K, V> {
         return this.storage.get(key);
     }
 
+    /**
+     * @returns {TDictionary<V>}
+     */
+    public getAsDictionary (): TDictionary<V> {
+        return Object.fromEntries(this.storage);
+    }
+
     /**
      * @param {K} key
      * @returns {V}

+ 31 - 0
src/storages/identifier-names-cache/IdentifierNamesCacheStorage.ts

@@ -0,0 +1,31 @@
+import { inject, injectable, postConstruct } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import { IIdentifierNamesCacheStorage } from '../../interfaces/storages/identifier-names-cache/IIdentifierNamesCacheStorage';
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+
+import { MapStorage } from '../MapStorage';
+
+@injectable()
+export class IdentifierNamesCacheStorage extends MapStorage <string, string> implements IIdentifierNamesCacheStorage {
+    /**
+     * @param {IRandomGenerator} randomGenerator
+     * @param {IOptions} options
+     */
+    public constructor (
+        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
+    ) {
+        super(randomGenerator, options);
+    }
+
+    @postConstruct()
+    public override initialize (): void {
+       super.initialize();
+
+       if (this.options.identifierNamesCache) {
+           this.storage = new Map(Object.entries(this.options.identifierNamesCache));
+       }
+    }
+}

+ 3 - 0
src/types/caches/TIdentifierNamesCache.ts

@@ -0,0 +1,3 @@
+import { TDictionary } from '../TDictionary';
+
+export type TIdentifierNamesCache = TDictionary<string>;

+ 26 - 14
test/dev/dev.ts

@@ -1,31 +1,43 @@
 'use strict';
 
+import { TIdentifierNamesCache } from '../../src/types/caches/TIdentifierNamesCache';
+
 import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
 
 (function () {
     const JavaScriptObfuscator: any = require('../../index');
 
-    let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+    const existingIdentifierNamesCache: TIdentifierNamesCache | null = {
+        foo: 'foo_existing',
+        bar: 'bar_existing'
+    };
+
+    let obfuscationResult = JavaScriptObfuscator.obfuscate(
         `
-            function foo(a, b, c, d) {
-              console.log(a, b, c, d)
-            }
-            
-            function bar(...args) {
-              foo(...args, 5)
-            }
-            
-            bar(...[1, 2, 3], 4)
+            (() => {
+                function foo(a, b, c, d) {
+                  console.log(a, b, c, d)
+                }
+                
+                function bar(...args) {
+                  foo(...args, 5)
+                }
+                
+                bar(...[1, 2, 3], 4)
+            })();
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
             compact: false,
-            controlFlowFlattening: true,
-            controlFlowFlatteningThreshold: 1,
-            identifierNamesGenerator: 'mangled'
+            identifierNamesCache: existingIdentifierNamesCache,
+            identifierNamesGenerator: 'mangled-shuffled'
         }
-    ).getObfuscatedCode();
+    );
+
+    let obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
+    let identifierNamesCache = obfuscationResult.getIdentifierNamesCache();
 
     console.log(obfuscatedCode);
     console.log(eval(obfuscatedCode));
+    console.log(identifierNamesCache);
 })();

+ 2 - 2
tsconfig.json

@@ -6,10 +6,10 @@
     "emitDecoratorMetadata": true,
     "experimentalDecorators": true,
     "lib": [
-      "es2017",
+      "es2019",
       "dom"
     ],
-    "target": "es2017",
+    "target": "es2018",
     "module": "commonjs",
     "resolveJsonModule": true,
     "esModuleInterop": true,

Some files were not shown because too many files changed in this diff