瀏覽代碼

Sync with dev

sanex 3 年之前
父節點
當前提交
ba3e4d6897
共有 63 個文件被更改,包括 2375 次插入1087 次删除
  1. 1 0
      .gitignore
  2. 18 1
      CHANGELOG.md
  3. 22 12
      README.md
  4. 0 0
      dist/index.browser.js
  5. 0 0
      dist/index.cli.js
  6. 0 0
      dist/index.js
  7. 20 19
      package.json
  8. 15 11
      src/JavaScriptObfuscator.ts
  9. 8 1
      src/cli/JavaScriptObfuscatorCLI.ts
  10. 13 3
      src/cli/utils/ObfuscatedCodeFileUtils.ts
  11. 1 2
      src/container/ServiceIdentifiers.ts
  12. 4 10
      src/container/modules/storages/StoragesModule.ts
  13. 12 21
      src/custom-nodes/string-array-nodes/StringArrayCallNode.ts
  14. 27 59
      src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode.ts
  15. 13 12
      src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperVariableNode.ts
  16. 1 0
      src/enums/node/NodeType.ts
  17. 9 0
      src/enums/source-map/SourceMapSourcesMode.ts
  18. 0 25
      src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeData.ts
  19. 4 2
      src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrappersData.ts
  20. 2 0
      src/interfaces/options/IOptions.ts
  21. 16 0
      src/interfaces/source-code/ISourceMap.ts
  22. 0 10
      src/interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeDataStorage.ts
  23. 0 10
      src/interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperNamesDataStorage.ts
  24. 10 0
      src/interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrappersDataStorage.ts
  25. 28 0
      src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts
  26. 2 0
      src/node-transformers/rename-properties-transformers/replacer/RenamePropertiesReplacer.ts
  27. 91 132
      src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts
  28. 63 119
      src/node-transformers/string-array-transformers/StringArrayTransformer.ts
  29. 24 0
      src/node/NodeGuards.ts
  30. 9 1
      src/options/Options.ts
  31. 2 0
      src/options/presets/Default.ts
  32. 2 0
      src/options/presets/NoCustomNodes.ts
  33. 18 0
      src/options/validators/IsInputFileName.ts
  34. 0 28
      src/storages/string-array-transformers/StringArrayScopeCallsWrapperLexicalScopeDataStorage.ts
  35. 5 5
      src/storages/string-array-transformers/StringArrayScopeCallsWrappersDataStorage.ts
  36. 0 7
      src/types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding.ts
  37. 7 0
      src/types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrappersDataByEncoding.ts
  38. 2 1
      test/declarations/index.d.ts
  39. 19 0
      test/declarations/source-map-resolve.d.ts
  40. 384 64
      test/functional-tests/cli/JavaScriptObfuscatorCLI.spec.ts
  41. 181 14
      test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts
  42. 234 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts
  43. 7 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/call-expression-1.js
  44. 7 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/call-expression-2.js
  45. 9 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/call-expression-3.js
  46. 10 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/call-expression-4.js
  47. 7 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/new-expression-1.js
  48. 7 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/new-expression-2.js
  49. 9 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/new-expression-3.js
  50. 10 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/new-expression-4.js
  51. 53 3
      test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/RenamePropertiesTransformer.spec.ts
  52. 4 0
      test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/fixtures/duplicated-generated-names-1.js
  53. 225 76
      test/functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/StringArrayScopeCallsWrapperTransformer.spec.ts
  54. 2 0
      test/functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/fixtures/function-calls-wrappers-different-indexes.js
  55. 18 0
      test/functional-tests/options/OptionsNormalizer.spec.ts
  56. 72 0
      test/functional-tests/options/input-file-name/Validation.spec.ts
  57. 7 0
      test/helpers/atob.ts
  58. 11 0
      test/helpers/parseSourceMapFromObfuscatedCode.ts
  59. 1 0
      test/index.spec.ts
  60. 19 10
      test/unit-tests/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.spec.ts
  61. 304 190
      test/unit-tests/cli/utils/ObfuscatedCodeFileUtils.spec.ts
  62. 44 8
      test/unit-tests/utils/CryptUtils.spec.ts
  63. 282 231
      yarn.lock

+ 1 - 0
.gitignore

@@ -11,3 +11,4 @@ npm-debug.log
 /tmp
 /tmp
 /test/benchmark/**/**
 /test/benchmark/**/**
 *dockerfile
 *dockerfile
+/test*.js

+ 18 - 1
CHANGELOG.md

@@ -1,9 +1,26 @@
 Change Log
 Change Log
 
 
-v2.16.0
+v2.18.0
 ---
 ---
 * Added support of `es2022` features: private identifiers and class properties
 * Added support of `es2022` features: private identifiers and class properties
 
 
+v2.17.0
+---
+* **New option**: `sourceMapSourcesMode` allows to control `sources` and `sourcesContent` fields of the source map
+* `inputFileName` option now required when using NodeJS API and `sourceMapSourcesMode` option has `sources` value`
+* Fixed some cases with wrong source map file name generation when `sourceMapFileName` option is set
+
+v2.16.0
+---
+* `stringArrayWrappersType: 'function'` now generates different indexes between each wrapper inside the same lexical scope
+* `stringArrayWrappersType: 'function'` now generates different parameters order between each wrapper inside the same lexical scope
+* `stringArrayWrappersType: 'function'` now appends `FunctionDeclaration` functions instead of `FunctionExpression` functions. This allows to append these wrappers at random positions inside each scope
+* `renameProperties` option now won't generate duplicated property names in some cases
+
+v2.15.6
+---
+* To increase performance and prevent possible runtime errors `transformObjectKeys` option now completely ignores objects with `CallExpression` or `NewExpression` nodes. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/948
+
 v2.15.5
 v2.15.5
 ---
 ---
 * Improved `stringArray` calls wrapper decode code
 * Improved `stringArray` calls wrapper decode code

+ 22 - 12
README.md

@@ -45,7 +45,7 @@ The example of obfuscated code: [github.com](https://github.com/javascript-obfus
 * (OpenCollective) https://opencollective.com/javascript-obfuscator
 * (OpenCollective) https://opencollective.com/javascript-obfuscator
 * PayPal credit card [https://www.paypal.com/donate](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&[email protected]&lc=US&no_note=0&item_name=Support+javascript-obfuscator&cn=&curency_code=USD&bn=PP-DonationsBF:btn_donateCC_LG.gif:NonHosted)
 * PayPal credit card [https://www.paypal.com/donate](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&[email protected]&lc=US&no_note=0&item_name=Support+javascript-obfuscator&cn=&curency_code=USD&bn=PP-DonationsBF:btn_donateCC_LG.gif:NonHosted)
 * PayPal https://www.paypal.me/javascriptobfuscator
 * PayPal https://www.paypal.me/javascriptobfuscator
-* (Bitcoin) 1Nv2773RDNzodHDxuxaYkTvwBkYRHmPhnG
+* (Bitcoin) bc1q203p8nyrstwm7vwzjg3h9l9t6y9ka0umw0rx96
 
 
 Huge thanks to all supporters!
 Huge thanks to all supporters!
 
 
@@ -377,6 +377,7 @@ Following options are available for the JS Obfuscator:
     sourceMapBaseUrl: '',
     sourceMapBaseUrl: '',
     sourceMapFileName: '',
     sourceMapFileName: '',
     sourceMapMode: 'separate',
     sourceMapMode: 'separate',
+    sourceMapSourcesMode: 'sources-content',
     splitStrings: false,
     splitStrings: false,
     splitStringsChunkLength: 10,
     splitStringsChunkLength: 10,
     stringArray: true,
     stringArray: true,
@@ -438,6 +439,7 @@ Following options are available for the JS Obfuscator:
     --source-map-base-url <string>
     --source-map-base-url <string>
     --source-map-file-name <string>
     --source-map-file-name <string>
     --source-map-mode <string> [inline, separate]
     --source-map-mode <string> [inline, separate]
+    --source-map-sources-mode <string> [sources, sources-content]
     --split-strings <boolean>
     --split-strings <boolean>
     --split-strings-chunk-length <number>
     --split-strings-chunk-length <number>
     --string-array <boolean>
     --string-array <boolean>
@@ -840,6 +842,7 @@ Prevents obfuscation of `require` imports. Could be helpful in some cases when f
 Type: `string` Default: `''`
 Type: `string` Default: `''`
 
 
 Allows to set name of the input file with source code. This name will be used internally for source map generation.
 Allows to set name of the input file with source code. This name will be used internally for source map generation.
+Required when using NodeJS API and `sourceMapSourcesMode` option has `sources` value`.
 
 
 ### `log`
 ### `log`
 Type: `boolean` Default: `false`
 Type: `boolean` Default: `false`
@@ -1071,6 +1074,13 @@ Specifies source map generation mode:
 * `inline` - add source map at the end of each .js files;
 * `inline` - add source map at the end of each .js files;
 * `separate` - generates corresponding '.map' file with source map. In case you run obfuscator through CLI - adds link to source map file to the end of file with obfuscated code `//# sourceMappingUrl=file.js.map`.
 * `separate` - generates corresponding '.map' file with source map. In case you run obfuscator through CLI - adds link to source map file to the end of file with obfuscated code `//# sourceMappingUrl=file.js.map`.
 
 
+### `sourceMapSourcesMode`
+Type: `string` Default: `sources-content`
+
+Allows to control `sources` and `sourcesContent` fields of the source map:
+* `sources-content` - adds dummy `sources` field, adds `sourcesContent` field with the original source code;
+* `sources` - adds `sources` field with a valid source description, does not add `sourcesContent` field. When using NodeJS API it's required to define `inputFileName` option that will be used as `sources` field value.
+
 ### `splitStrings`
 ### `splitStrings`
 Type: `boolean` Default: `false`
 Type: `boolean` Default: `false`
 
 
@@ -1281,8 +1291,8 @@ Type: `string` Default: `variable`
 Allows to select a type of the wrappers that are appending by the `stringArrayWrappersCount` option.
 Allows to select a type of the wrappers that are appending by the `stringArrayWrappersCount` option.
 
 
 Available values:
 Available values:
-* `'variable'`: appends variable wrappers. Fast performance.
-* `'function'`: appends function wrappers. Slower performance than with `variable` but provides more strict obfuscation
+* `'variable'`: appends variable wrappers at the top of each scope. Fast performance.
+* `'function'`: appends function wrappers at random positions inside each scope. Slower performance than with `variable` but provides more strict obfuscation.
 
 
 Highly recommended to use `function` wrappers for higher obfuscation when a performance loss doesn't have a high impact on an obfuscated application.
 Highly recommended to use `function` wrappers for higher obfuscation when a performance loss doesn't have a high impact on an obfuscated application.
 
 
@@ -1304,9 +1314,6 @@ const a = [
     'bar',
     'bar',
     'foo'
     'foo'
 ];
 ];
-const d = function (c, g) {
-    return b(g - 0x3e1, c);
-};
 const foo = d(0x567, 0x568);
 const foo = d(0x567, 0x568);
 function b(c, d) {
 function b(c, d) {
     b = function (e, f) {
     b = function (e, f) {
@@ -1317,14 +1324,17 @@ function b(c, d) {
     return b(c, d);
     return b(c, d);
 }
 }
 function test() {
 function test() {
-    const e = function (c, g) {
-        return b(c - 0x396, g);
-    };
-    const f = function (c, g) {
-        return b(c - 0x396, g);
-    };
     const c = e(0x51c, 0x51b);
     const c = e(0x51c, 0x51b);
+    function e (c, g) {
+        return b(c - 0x396, g);
+    }
     console[f(0x51b, 0x51d)](foo, c);
     console[f(0x51b, 0x51d)](foo, c);
+    function f (c, g) {
+        return b(c - 0x396, g);
+    }
+}
+function d (c, g) {
+    return b(g - 0x3e1, c);
 }
 }
 test();
 test();
 ```
 ```

文件差異過大導致無法顯示
+ 0 - 0
dist/index.browser.js


文件差異過大導致無法顯示
+ 0 - 0
dist/index.cli.js


文件差異過大導致無法顯示
+ 0 - 0
dist/index.js


+ 20 - 19
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "javascript-obfuscator",
   "name": "javascript-obfuscator",
-  "version": "2.16.0",
+  "version": "2.18.0",
   "description": "JavaScript obfuscator",
   "description": "JavaScript obfuscator",
   "keywords": [
   "keywords": [
     "obfuscator",
     "obfuscator",
@@ -30,7 +30,7 @@
     "chance": "1.1.7",
     "chance": "1.1.7",
     "class-validator": "0.13.1",
     "class-validator": "0.13.1",
     "commander": "8.0.0",
     "commander": "8.0.0",
-    "eslint-scope": "github:eslint/eslint-scope#master",
+    "eslint-scope": "6.0.0",
     "fast-deep-equal": "3.1.3",
     "fast-deep-equal": "3.1.3",
     "inversify": "5.1.1",
     "inversify": "5.1.1",
     "js-string-escape": "1.0.1",
     "js-string-escape": "1.0.1",
@@ -46,30 +46,30 @@
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@istanbuljs/nyc-config-typescript": "1.0.1",
     "@istanbuljs/nyc-config-typescript": "1.0.1",
-    "@types/chai": "4.2.19",
-    "@types/chance": "1.1.2",
-    "@types/escodegen": "0.0.6",
-    "@types/eslint-scope": "3.7.0",
-    "@types/estraverse": "5.1.0",
-    "@types/estree": "0.0.49",
+    "@types/chai": "4.2.21",
+    "@types/chance": "1.1.3",
+    "@types/escodegen": "0.0.7",
+    "@types/eslint-scope": "3.7.1",
+    "@types/estraverse": "5.1.1",
+    "@types/estree": "0.0.50",
     "@types/js-string-escape": "1.0.0",
     "@types/js-string-escape": "1.0.0",
-    "@types/md5": "2.3.0",
-    "@types/mkdirp": "1.0.1",
-    "@types/mocha": "8.2.2",
+    "@types/md5": "2.3.1",
+    "@types/mkdirp": "1.0.2",
+    "@types/mocha": "9.0.0",
     "@types/multimatch": "4.0.0",
     "@types/multimatch": "4.0.0",
-    "@types/node": "16.0.0",
-    "@types/rimraf": "3.0.0",
+    "@types/node": "16.4.1",
+    "@types/rimraf": "3.0.1",
     "@types/sinon": "10.0.2",
     "@types/sinon": "10.0.2",
     "@types/string-template": "1.0.2",
     "@types/string-template": "1.0.2",
     "@types/webpack-env": "1.16.2",
     "@types/webpack-env": "1.16.2",
-    "@typescript-eslint/eslint-plugin": "4.28.1",
-    "@typescript-eslint/parser": "4.28.1",
+    "@typescript-eslint/eslint-plugin": "4.28.4",
+    "@typescript-eslint/parser": "4.28.4",
     "chai": "4.3.4",
     "chai": "4.3.4",
     "chai-exclude": "2.0.3",
     "chai-exclude": "2.0.3",
     "cross-env": "7.0.3",
     "cross-env": "7.0.3",
-    "eslint": "7.30.0",
+    "eslint": "7.31.0",
     "eslint-plugin-import": "2.23.4",
     "eslint-plugin-import": "2.23.4",
-    "eslint-plugin-jsdoc": "35.4.1",
+    "eslint-plugin-jsdoc": "35.5.1",
     "eslint-plugin-no-null": "1.0.2",
     "eslint-plugin-no-null": "1.0.2",
     "eslint-plugin-prefer-arrow": "1.2.3",
     "eslint-plugin-prefer-arrow": "1.2.3",
     "eslint-plugin-unicorn": "34.0.1",
     "eslint-plugin-unicorn": "34.0.1",
@@ -81,11 +81,12 @@
     "pre-commit": "1.2.2",
     "pre-commit": "1.2.2",
     "rimraf": "3.0.2",
     "rimraf": "3.0.2",
     "sinon": "11.1.1",
     "sinon": "11.1.1",
+    "source-map-resolve": "^0.6.0",
     "threads": "1.6.5",
     "threads": "1.6.5",
     "ts-loader": "9.2.3",
     "ts-loader": "9.2.3",
-    "ts-node": "10.0.0",
+    "ts-node": "10.1.0",
     "typescript": "4.4.0-beta",
     "typescript": "4.4.0-beta",
-    "webpack": "5.42.0",
+    "webpack": "5.46.0",
     "webpack-cli": "4.7.2",
     "webpack-cli": "4.7.2",
     "webpack-node-externals": "3.0.0"
     "webpack-node-externals": "3.0.0"
   },
   },

+ 15 - 11
src/JavaScriptObfuscator.ts

@@ -27,6 +27,7 @@ import { ecmaVersion } from './constants/EcmaVersion';
 import { ASTParserFacade } from './ASTParserFacade';
 import { ASTParserFacade } from './ASTParserFacade';
 import { NodeGuards } from './node/NodeGuards';
 import { NodeGuards } from './node/NodeGuards';
 import { Utils } from './utils/Utils';
 import { Utils } from './utils/Utils';
+import { SourceMapSourcesMode } from './enums/source-map/SourceMapSourcesMode';
 
 
 @injectable()
 @injectable()
 export class JavaScriptObfuscator implements IJavaScriptObfuscator {
 export class JavaScriptObfuscator implements IJavaScriptObfuscator {
@@ -247,20 +248,23 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
      */
      */
     private generateCode (sourceCode: string, astTree: ESTree.Program): IGeneratorOutput {
     private generateCode (sourceCode: string, astTree: ESTree.Program): IGeneratorOutput {
         const escodegenParams: escodegen.GenerateOptions = {
         const escodegenParams: escodegen.GenerateOptions = {
-            ...JavaScriptObfuscator.escodegenParams
-        };
-
-        if (this.options.sourceMap) {
-            escodegenParams.sourceMap = this.options.inputFileName || 'sourceMap';
-            escodegenParams.sourceContent = sourceCode;
-        }
-
-        const generatorOutput: IGeneratorOutput = escodegen.generate(astTree, {
-            ...escodegenParams,
+            ...JavaScriptObfuscator.escodegenParams,
             format: {
             format: {
                 compact: this.options.compact
                 compact: this.options.compact
+            },
+            ...this.options.sourceMap && {
+                ...this.options.sourceMapSourcesMode === SourceMapSourcesMode.SourcesContent
+                    ? {
+                        sourceMap: 'sourceMap',
+                        sourceContent: sourceCode
+                    }
+                    : {
+                        sourceMap: this.options.inputFileName || 'sourceMap'
+                    }
             }
             }
-        });
+        };
+
+        const generatorOutput: IGeneratorOutput = escodegen.generate(astTree, escodegenParams);
 
 
         generatorOutput.map = generatorOutput.map ? generatorOutput.map.toString() : '';
         generatorOutput.map = generatorOutput.map ? generatorOutput.map.toString() : '';
 
 

+ 8 - 1
src/cli/JavaScriptObfuscatorCLI.ts

@@ -17,6 +17,7 @@ import { ObfuscationTarget } from '../enums/ObfuscationTarget';
 import { OptionsPreset } from '../enums/options/presets/OptionsPreset';
 import { OptionsPreset } from '../enums/options/presets/OptionsPreset';
 import { RenamePropertiesMode } from '../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { RenamePropertiesMode } from '../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { SourceMapMode } from '../enums/source-map/SourceMapMode';
 import { SourceMapMode } from '../enums/source-map/SourceMapMode';
+import { SourceMapSourcesMode } from '../enums/source-map/SourceMapSourcesMode';
 import { StringArrayEncoding } from '../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayEncoding } from '../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayWrappersType } from '../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 import { StringArrayWrappersType } from '../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
@@ -53,7 +54,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
     public static readonly obfuscatedFilePrefix: string = '-obfuscated';
     public static readonly obfuscatedFilePrefix: string = '-obfuscated';
 
 
     /**
     /**
-     * @type {commander.CommanderStatic}
+     * @type {commander.Command}
      */
      */
     @initializable()
     @initializable()
     private commands!: commander.Command;
     private commands!: commander.Command;
@@ -352,6 +353,12 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 `Values: ${CLIUtils.stringifyOptionAvailableValues(SourceMapMode)}. ` +
                 `Values: ${CLIUtils.stringifyOptionAvailableValues(SourceMapMode)}. ` +
                 `Default: ${SourceMapMode.Separate}`
                 `Default: ${SourceMapMode.Separate}`
             )
             )
+            .option(
+                '--source-map-sources-mode <string>',
+                'Specify source map sources mode. ' +
+                `Values: ${CLIUtils.stringifyOptionAvailableValues(SourceMapSourcesMode)}. ` +
+                `Default: ${SourceMapSourcesMode.SourcesContent}`
+            )
             .option(
             .option(
                 '--split-strings <boolean>',
                 '--split-strings <boolean>',
                 'Splits literal strings into chunks with length of `splitStringsChunkLength` option value',
                 'Splits literal strings into chunks with length of `splitStringsChunkLength` option value',

+ 13 - 3
src/cli/utils/ObfuscatedCodeFileUtils.ts

@@ -98,9 +98,19 @@ export class ObfuscatedCodeFileUtils {
 
 
         if (sourceMapFileName) {
         if (sourceMapFileName) {
             const indexOfLastSeparator: number = normalizedOutputCodePath.lastIndexOf(path.sep);
             const indexOfLastSeparator: number = normalizedOutputCodePath.lastIndexOf(path.sep);
-            const sourceMapPath: string = parsedOutputCodePath.ext && indexOfLastSeparator > 0
-                ? normalizedOutputCodePath.slice(0, indexOfLastSeparator)
-                : normalizedOutputCodePath;
+            let sourceMapPath: string;
+
+            if (parsedOutputCodePath.ext) {
+                // File path with directory, like: `foo/bar.js`, or without, like: `bar.js`
+                const isFilePathWithDirectory: boolean = indexOfLastSeparator > 0;
+
+                sourceMapPath = isFilePathWithDirectory
+                    ? normalizedOutputCodePath.slice(0, indexOfLastSeparator)
+                    : '';
+            } else {
+                sourceMapPath = normalizedOutputCodePath;
+            }
+
             // remove possible drive letter for win32 environment
             // remove possible drive letter for win32 environment
             const normalizedSourceMapFilePath: string = sourceMapFileName.replace(/^[a-zA-Z]:\\*/, '');
             const normalizedSourceMapFilePath: string = sourceMapFileName.replace(/^[a-zA-Z]:\\*/, '');
 
 

+ 1 - 2
src/container/ServiceIdentifiers.ts

@@ -53,8 +53,7 @@ export enum ServiceIdentifiers {
     ISourceCode = 'ISourceCode',
     ISourceCode = 'ISourceCode',
     IScopeAnalyzer = 'IScopeAnalyzer',
     IScopeAnalyzer = 'IScopeAnalyzer',
     IStringArrayIndexNode = 'IStringArrayIndexNode',
     IStringArrayIndexNode = 'IStringArrayIndexNode',
-    IStringArrayScopeCallsWrapperLexicalScopeDataStorage = 'IStringArrayScopeCallsWrapperLexicalScopeDataStorage',
-    IStringArrayScopeCallsWrapperNamesDataStorage = 'IStringArrayScopeCallsWrapperNamesDataStorage',
+    IStringArrayScopeCallsWrappersDataStorage = 'IStringArrayScopeCallsWrappersDataStorage',
     IStringArrayStorage = 'IStringArrayStorage',
     IStringArrayStorage = 'IStringArrayStorage',
     IStringArrayStorageAnalyzer = 'IStringArrayStorageAnalyzer',
     IStringArrayStorageAnalyzer = 'IStringArrayStorageAnalyzer',
     IThroughIdentifierReplacer = 'IThroughIdentifierReplacer',
     IThroughIdentifierReplacer = 'IThroughIdentifierReplacer',

+ 4 - 10
src/container/modules/storages/StoragesModule.ts

@@ -9,8 +9,7 @@ import { ILiteralNodesCacheStorage } from '../../../interfaces/storages/string-a
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IPropertyIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage';
 import { IPropertyIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
-import { IStringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeDataStorage';
-import { IStringArrayScopeCallsWrapperNamesDataStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperNamesDataStorage';
+import { IStringArrayScopeCallsWrappersDataStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrappersDataStorage';
 import { IStringArrayStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IStringArrayStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IVisitedLexicalScopeNodesStackStorage } from '../../../interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage';
 import { IVisitedLexicalScopeNodesStackStorage } from '../../../interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage';
 
 
@@ -19,8 +18,7 @@ import { CustomCodeHelperGroupStorage } from '../../../storages/custom-code-help
 import { GlobalIdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/GlobalIdentifierNamesCacheStorage';
 import { GlobalIdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/GlobalIdentifierNamesCacheStorage';
 import { LiteralNodesCacheStorage } from '../../../storages/string-array-transformers/LiteralNodesCacheStorage';
 import { LiteralNodesCacheStorage } from '../../../storages/string-array-transformers/LiteralNodesCacheStorage';
 import { PropertyIdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/PropertyIdentifierNamesCacheStorage';
 import { PropertyIdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/PropertyIdentifierNamesCacheStorage';
-import { StringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrapperLexicalScopeDataStorage';
-import { StringArrayScopeCallsWrapperNamesDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrapperNamesDataStorage';
+import { StringArrayScopeCallsWrappersDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrappersDataStorage';
 import { StringArrayStorage } from '../../../storages/string-array-transformers/StringArrayStorage';
 import { StringArrayStorage } from '../../../storages/string-array-transformers/StringArrayStorage';
 import { VisitedLexicalScopeNodesStackStorage } from '../../../storages/string-array-transformers/VisitedLexicalScopeNodesStackStorage';
 import { VisitedLexicalScopeNodesStackStorage } from '../../../storages/string-array-transformers/VisitedLexicalScopeNodesStackStorage';
 
 
@@ -46,12 +44,8 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b
         .to(StringArrayStorage)
         .to(StringArrayStorage)
         .inSingletonScope();
         .inSingletonScope();
 
 
-    bind<IStringArrayScopeCallsWrapperLexicalScopeDataStorage>(ServiceIdentifiers.IStringArrayScopeCallsWrapperLexicalScopeDataStorage)
-        .to(StringArrayScopeCallsWrapperLexicalScopeDataStorage)
-        .inSingletonScope();
-
-    bind<IStringArrayScopeCallsWrapperNamesDataStorage>(ServiceIdentifiers.IStringArrayScopeCallsWrapperNamesDataStorage)
-        .to(StringArrayScopeCallsWrapperNamesDataStorage)
+    bind<IStringArrayScopeCallsWrappersDataStorage>(ServiceIdentifiers.IStringArrayScopeCallsWrappersDataStorage)
+        .to(StringArrayScopeCallsWrappersDataStorage)
         .inSingletonScope();
         .inSingletonScope();
 
 
     bind<IVisitedLexicalScopeNodesStackStorage>(ServiceIdentifiers.IVisitedLexicalScopeNodesStackStorage)
     bind<IVisitedLexicalScopeNodesStackStorage>(ServiceIdentifiers.IVisitedLexicalScopeNodesStackStorage)

+ 12 - 21
src/custom-nodes/string-array-nodes/StringArrayCallNode.ts

@@ -11,7 +11,6 @@ import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
-import { IStringArrayScopeCallsWrapperParameterIndexesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 
 
 import { initializable } from '../../decorators/Initializable';
 import { initializable } from '../../decorators/Initializable';
@@ -19,6 +18,7 @@ import { initializable } from '../../decorators/Initializable';
 import { AbstractStringArrayCallNode } from './AbstractStringArrayCallNode';
 import { AbstractStringArrayCallNode } from './AbstractStringArrayCallNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeUtils } from '../../node/NodeUtils';
 import { NodeUtils } from '../../node/NodeUtils';
+import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
 
 
 @injectable()
 @injectable()
 export class StringArrayCallNode extends AbstractStringArrayCallNode {
 export class StringArrayCallNode extends AbstractStringArrayCallNode {
@@ -41,16 +41,10 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
     private indexShiftAmount!: number;
     private indexShiftAmount!: number;
 
 
     /**
     /**
-     * @type {string}
+     * @type {IStringArrayScopeCallsWrapperData}
      */
      */
     @initializable()
     @initializable()
-    private stringArrayCallsWrapperName!: string;
-
-    /**
-     * @type {IStringArrayScopeCallsWrapperParameterIndexesData | null}
-     */
-    @initializable()
-    private stringArrayCallsWrapperParameterIndexesData!: IStringArrayScopeCallsWrapperParameterIndexesData | null;
+    private stringArrayCallsWrapperData!: IStringArrayScopeCallsWrapperData;
 
 
     /**
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
@@ -84,23 +78,20 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
     }
     }
 
 
     /**
     /**
-     * @param {string} stringArrayCallsWrapperName
-     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} stringArrayCallsWrapperParameterIndexesData
      * @param {number} index
      * @param {number} index
      * @param {number} indexShiftAmount
      * @param {number} indexShiftAmount
+     * @param {IStringArrayScopeCallsWrapperData} stringArrayCallsWrapperData
      * @param {string | null} decodeKey
      * @param {string | null} decodeKey
      */
      */
     public initialize (
     public initialize (
-        stringArrayCallsWrapperName: string,
-        stringArrayCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null,
         index: number,
         index: number,
         indexShiftAmount: number,
         indexShiftAmount: number,
+        stringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData,
         decodeKey: string | null
         decodeKey: string | null
     ): void {
     ): void {
-        this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
-        this.stringArrayCallsWrapperParameterIndexesData = stringArrayCallsWrapperParameterIndexesData;
         this.index = index;
         this.index = index;
         this.indexShiftAmount = indexShiftAmount;
         this.indexShiftAmount = indexShiftAmount;
+        this.stringArrayCallsWrapperData = stringArrayCallsWrapperData;
         this.decodeKey = decodeKey;
         this.decodeKey = decodeKey;
     }
     }
 
 
@@ -108,7 +99,7 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
      * @returns {TStatement[]}
      * @returns {TStatement[]}
      */
      */
     protected getNodeStructure (): TStatement[] {
     protected getNodeStructure (): TStatement[] {
-        const resultIndex: number = this.indexShiftAmount + this.index;
+        const resultIndex: number = this.indexShiftAmount + this.stringArrayCallsWrapperData.index + this.index;
 
 
         const indexNode: ESTree.Expression = this.getStringArrayIndexNode(resultIndex);
         const indexNode: ESTree.Expression = this.getStringArrayIndexNode(resultIndex);
         const rc4KeyLiteralNode: ESTree.Literal | null = this.decodeKey
         const rc4KeyLiteralNode: ESTree.Literal | null = this.decodeKey
@@ -117,7 +108,7 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
 
 
         // filling call expression arguments with a fake arguments first
         // filling call expression arguments with a fake arguments first
         const callExpressionArgs: ESTree.Expression[] = this.arrayUtils.fillWithRange(
         const callExpressionArgs: ESTree.Expression[] = this.arrayUtils.fillWithRange(
-            !this.stringArrayCallsWrapperParameterIndexesData
+            !this.stringArrayCallsWrapperData.parameterIndexesData
                 // root string array calls wrapper
                 // root string array calls wrapper
                 ? AbstractStringArrayCallNode.stringArrayRootCallsWrapperParametersCount
                 ? AbstractStringArrayCallNode.stringArrayRootCallsWrapperParametersCount
                 // scope string array calls wrapper
                 // scope string array calls wrapper
@@ -126,14 +117,14 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
         );
         );
 
 
         callExpressionArgs.splice(
         callExpressionArgs.splice(
-            this.stringArrayCallsWrapperParameterIndexesData?.valueIndexParameterIndex ?? 0,
+            this.stringArrayCallsWrapperData.parameterIndexesData?.valueIndexParameterIndex ?? 0,
             1,
             1,
             indexNode
             indexNode
         );
         );
 
 
-        if (this.stringArrayCallsWrapperParameterIndexesData) {
+        if (this.stringArrayCallsWrapperData.parameterIndexesData) {
             callExpressionArgs.splice(
             callExpressionArgs.splice(
-                this.stringArrayCallsWrapperParameterIndexesData.decodeKeyParameterIndex,
+                this.stringArrayCallsWrapperData.parameterIndexesData.decodeKeyParameterIndex,
                 1,
                 1,
                 // use rc4 key literal node if exists or a node with fake string array wrapper index
                 // use rc4 key literal node if exists or a node with fake string array wrapper index
                 rc4KeyLiteralNode ?? this.getFakeStringArrayIndexNode(resultIndex)
                 rc4KeyLiteralNode ?? this.getFakeStringArrayIndexNode(resultIndex)
@@ -147,7 +138,7 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
 
 
         const structure: TStatement = NodeFactory.expressionStatementNode(
         const structure: TStatement = NodeFactory.expressionStatementNode(
             NodeFactory.callExpressionNode(
             NodeFactory.callExpressionNode(
-                NodeFactory.identifierNode(this.stringArrayCallsWrapperName),
+                NodeFactory.identifierNode(this.stringArrayCallsWrapperData.name),
                 callExpressionArgs
                 callExpressionArgs
             )
             )
         );
         );

+ 27 - 59
src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode.ts

@@ -11,7 +11,7 @@ import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
-import { IStringArrayScopeCallsWrapperParameterIndexesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData';
+import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 
 
 import { initializable } from '../../decorators/Initializable';
 import { initializable } from '../../decorators/Initializable';
@@ -23,35 +23,16 @@ import { NodeUtils } from '../../node/NodeUtils';
 @injectable()
 @injectable()
 export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArrayCallNode {
 export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArrayCallNode {
     /**
     /**
-     * @type {number}
+     * @type {IStringArrayScopeCallsWrapperData}
      */
      */
     @initializable()
     @initializable()
-    private shiftedIndex!: number;
+    private upperStringArrayCallsWrapperData!: IStringArrayScopeCallsWrapperData;
 
 
     /**
     /**
-     * @type {string}
+     * @type {IStringArrayScopeCallsWrapperData}
      */
      */
     @initializable()
     @initializable()
-    private upperStringArrayCallsWrapperName!: string;
-
-    /**
-     * @type {IStringArrayScopeCallsWrapperParameterIndexesData}
-     */
-    @initializable()
-    private upperStringArrayCallsWrapperParameterIndexesData!: IStringArrayScopeCallsWrapperParameterIndexesData | null;
-
-    /**
-     * @type {string}
-     */
-    @initializable()
-    private stringArrayScopeCallsWrapperName!: string;
-
-    /**
-     * @type {IStringArrayScopeCallsWrapperParameterIndexesData}
-     */
-    @initializable()
-    private stringArrayScopeCallsWrapperParameterIndexesData!: IStringArrayScopeCallsWrapperParameterIndexesData | null;
-
+    private stringArrayScopeCallsWrapperData!: IStringArrayScopeCallsWrapperData;
 
 
     /**
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
@@ -85,24 +66,15 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
     }
     }
 
 
     /**
     /**
-     * @param {string} stringArrayScopeCallsWrapperName
-     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} stringArrayScopeCallsWrapperParameterIndexesData
-     * @param {string} upperStringArrayCallsWrapperName
-     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} upperStringArrayCallsWrapperParameterIndexesData
-     * @param {number} shiftedIndex
+     * @param {IStringArrayScopeCallsWrapperData} stringArrayScopeCallsWrapperData
+     * @param {IStringArrayScopeCallsWrapperData} upperStringArrayCallsWrapperData
      */
      */
     public initialize (
     public initialize (
-        stringArrayScopeCallsWrapperName: string,
-        stringArrayScopeCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null,
-        upperStringArrayCallsWrapperName: string,
-        upperStringArrayCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null,
-        shiftedIndex: number,
+        stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData,
+        upperStringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData,
     ): void {
     ): void {
-        this.stringArrayScopeCallsWrapperName = stringArrayScopeCallsWrapperName;
-        this.stringArrayScopeCallsWrapperParameterIndexesData = stringArrayScopeCallsWrapperParameterIndexesData;
-        this.upperStringArrayCallsWrapperName = upperStringArrayCallsWrapperName;
-        this.upperStringArrayCallsWrapperParameterIndexesData = upperStringArrayCallsWrapperParameterIndexesData;
-        this.shiftedIndex = shiftedIndex;
+        this.stringArrayScopeCallsWrapperData = stringArrayScopeCallsWrapperData;
+        this.upperStringArrayCallsWrapperData = upperStringArrayCallsWrapperData;
     }
     }
 
 
     /**
     /**
@@ -116,13 +88,16 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
 
 
         const stringArrayCallNode: ESTree.Expression = this.getUpperStringArrayCallNode(
         const stringArrayCallNode: ESTree.Expression = this.getUpperStringArrayCallNode(
             stringArrayCallIdentifierNode,
             stringArrayCallIdentifierNode,
-            this.getStringArrayIndexNode(this.shiftedIndex)
+            this.getStringArrayIndexNode(
+                this.stringArrayScopeCallsWrapperData.index
+                - this.upperStringArrayCallsWrapperData.index
+            )
         );
         );
 
 
         // stage 1: function expression node parameters
         // stage 1: function expression node parameters
         // filling all parameters with a fake parameters first
         // filling all parameters with a fake parameters first
         const parameters: ESTree.Identifier[] = this.arrayUtils.fillWithRange(
         const parameters: ESTree.Identifier[] = this.arrayUtils.fillWithRange(
-            !this.stringArrayScopeCallsWrapperParameterIndexesData
+            !this.stringArrayScopeCallsWrapperData.parameterIndexesData
                 // root string array calls wrapper
                 // root string array calls wrapper
                 ? AbstractStringArrayCallNode.stringArrayRootCallsWrapperParametersCount
                 ? AbstractStringArrayCallNode.stringArrayRootCallsWrapperParametersCount
                 // scope string array calls wrapper
                 // scope string array calls wrapper
@@ -130,12 +105,12 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
             () => this.getFakeParameterNode()
             () => this.getFakeParameterNode()
         );
         );
         parameters.splice(
         parameters.splice(
-            this.stringArrayScopeCallsWrapperParameterIndexesData?.valueIndexParameterIndex ?? 0,
+            this.stringArrayScopeCallsWrapperData.parameterIndexesData?.valueIndexParameterIndex ?? 0,
             1,
             1,
             stringArrayCallIdentifierNode
             stringArrayCallIdentifierNode
         );
         );
         parameters.splice(
         parameters.splice(
-            this.stringArrayScopeCallsWrapperParameterIndexesData?.decodeKeyParameterIndex ?? 1,
+            this.stringArrayScopeCallsWrapperData.parameterIndexesData?.decodeKeyParameterIndex ?? 1,
             1,
             1,
             decodeKeyIdentifierNode
             decodeKeyIdentifierNode
         );
         );
@@ -143,7 +118,7 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
         // stage 2: upper string array call expression arguments
         // stage 2: upper string array call expression arguments
         // filling all call expression arguments with a fake string array calls
         // filling all call expression arguments with a fake string array calls
         const callExpressionArgs: ESTree.Expression[] = this.arrayUtils.fillWithRange(
         const callExpressionArgs: ESTree.Expression[] = this.arrayUtils.fillWithRange(
-            !this.upperStringArrayCallsWrapperParameterIndexesData
+            !this.upperStringArrayCallsWrapperData.parameterIndexesData
                 // root string array calls wrapper
                 // root string array calls wrapper
                 ? AbstractStringArrayCallNode.stringArrayRootCallsWrapperParametersCount
                 ? AbstractStringArrayCallNode.stringArrayRootCallsWrapperParametersCount
                 // scope string array calls wrapper
                 // scope string array calls wrapper
@@ -155,45 +130,38 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
         );
         );
 
 
         callExpressionArgs.splice(
         callExpressionArgs.splice(
-            this.upperStringArrayCallsWrapperParameterIndexesData?.valueIndexParameterIndex ?? 0,
+            this.upperStringArrayCallsWrapperData.parameterIndexesData?.valueIndexParameterIndex ?? 0,
             1,
             1,
             stringArrayCallNode
             stringArrayCallNode
         );
         );
         callExpressionArgs.splice(
         callExpressionArgs.splice(
-            this.upperStringArrayCallsWrapperParameterIndexesData?.decodeKeyParameterIndex ?? 1,
+            this.upperStringArrayCallsWrapperData.parameterIndexesData?.decodeKeyParameterIndex ?? 1,
             1,
             1,
             decodeKeyIdentifierNode
             decodeKeyIdentifierNode
         );
         );
 
 
-        // stage 3: function expression node
-        const functionExpressionNode: ESTree.FunctionExpression =  NodeFactory.functionExpressionNode(
+        // stage 3: function declaration node
+        const functionDeclarationNode: ESTree.FunctionDeclaration =  NodeFactory.functionDeclarationNode(
+            this.stringArrayScopeCallsWrapperData.name,
             parameters,
             parameters,
             NodeFactory.blockStatementNode([
             NodeFactory.blockStatementNode([
                 NodeFactory.returnStatementNode(
                 NodeFactory.returnStatementNode(
                     NodeFactory.callExpressionNode(
                     NodeFactory.callExpressionNode(
-                        NodeFactory.identifierNode(this.upperStringArrayCallsWrapperName),
+                        NodeFactory.identifierNode(this.upperStringArrayCallsWrapperData.name),
                         callExpressionArgs
                         callExpressionArgs
                     )
                     )
                 )
                 )
             ])
             ])
         );
         );
 
 
-        const structure: TStatement = NodeFactory.variableDeclarationNode(
-            [
-                NodeFactory.variableDeclaratorNode(
-                    NodeFactory.identifierNode(this.stringArrayScopeCallsWrapperName),
-                    functionExpressionNode
-                )
-            ],
-            'const',
-        );
+        const structure: TStatement = functionDeclarationNode;
 
 
         NodeUtils.parentizeAst(structure);
         NodeUtils.parentizeAst(structure);
 
 
         // stage 4: rename
         // stage 4: rename
         // have to generate names for both parameter and call identifiers
         // have to generate names for both parameter and call identifiers
         for (const parameter of parameters) {
         for (const parameter of parameters) {
-            parameter.name = this.identifierNamesGenerator.generateForLexicalScope(functionExpressionNode);
+            parameter.name = this.identifierNamesGenerator.generateForLexicalScope(functionDeclarationNode);
         }
         }
 
 
         return [structure];
         return [structure];

+ 13 - 12
src/custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperVariableNode.ts

@@ -9,6 +9,7 @@ import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 
 
 import { initializable } from '../../decorators/Initializable';
 import { initializable } from '../../decorators/Initializable';
@@ -20,16 +21,16 @@ import { NodeUtils } from '../../node/NodeUtils';
 @injectable()
 @injectable()
 export class StringArrayScopeCallsWrapperVariableNode extends AbstractStringArrayCallNode {
 export class StringArrayScopeCallsWrapperVariableNode extends AbstractStringArrayCallNode {
     /**
     /**
-     * @type {string}
+     * @type {IStringArrayScopeCallsWrapperData}
      */
      */
     @initializable()
     @initializable()
-    private stringArrayCallsWrapperName!: string;
+    private stringArrayCallsWrapperData!: IStringArrayScopeCallsWrapperData;
 
 
     /**
     /**
-     * @type {string}
+     * @type {IStringArrayScopeCallsWrapperData}
      */
      */
     @initializable()
     @initializable()
-    private stringArrayScopeCallsWrapperName!: string;
+    private stringArrayScopeCallsWrapperData!: IStringArrayScopeCallsWrapperData;
 
 
 
 
     /**
     /**
@@ -64,15 +65,15 @@ export class StringArrayScopeCallsWrapperVariableNode extends AbstractStringArra
     }
     }
 
 
     /**
     /**
-     * @param {string} stringArrayScopeCallsWrapperName
-     * @param {string} stringArrayCallsWrapperName
+     * @param {IStringArrayScopeCallsWrapperData} stringArrayScopeCallsWrapperData
+     * @param {IStringArrayScopeCallsWrapperData} stringArrayCallsWrapperData
      */
      */
     public initialize (
     public initialize (
-        stringArrayScopeCallsWrapperName: string,
-        stringArrayCallsWrapperName: string
+        stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData,
+        stringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData
     ): void {
     ): void {
-        this.stringArrayScopeCallsWrapperName = stringArrayScopeCallsWrapperName;
-        this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
+        this.stringArrayScopeCallsWrapperData = stringArrayScopeCallsWrapperData;
+        this.stringArrayCallsWrapperData = stringArrayCallsWrapperData;
     }
     }
 
 
     /**
     /**
@@ -82,8 +83,8 @@ export class StringArrayScopeCallsWrapperVariableNode extends AbstractStringArra
         const structure: TStatement = NodeFactory.variableDeclarationNode(
         const structure: TStatement = NodeFactory.variableDeclarationNode(
             [
             [
                 NodeFactory.variableDeclaratorNode(
                 NodeFactory.variableDeclaratorNode(
-                    NodeFactory.identifierNode(this.stringArrayScopeCallsWrapperName),
-                    NodeFactory.identifierNode(this.stringArrayCallsWrapperName)
+                    NodeFactory.identifierNode(this.stringArrayScopeCallsWrapperData.name),
+                    NodeFactory.identifierNode(this.stringArrayCallsWrapperData.name)
                 )
                 )
             ],
             ],
             'const',
             'const',

+ 1 - 0
src/enums/node/NodeType.ts

@@ -35,6 +35,7 @@ export enum NodeType {
     LogicalExpression = 'LogicalExpression',
     LogicalExpression = 'LogicalExpression',
     MemberExpression = 'MemberExpression',
     MemberExpression = 'MemberExpression',
     MethodDefinition = 'MethodDefinition',
     MethodDefinition = 'MethodDefinition',
+    NewExpression = 'NewExpression',
     ObjectExpression = 'ObjectExpression',
     ObjectExpression = 'ObjectExpression',
     ObjectPattern = 'ObjectPattern',
     ObjectPattern = 'ObjectPattern',
     Program = 'Program',
     Program = 'Program',

+ 9 - 0
src/enums/source-map/SourceMapSourcesMode.ts

@@ -0,0 +1,9 @@
+import { Utils } from '../../utils/Utils';
+
+export const SourceMapSourcesMode: Readonly<{
+    Sources: 'sources';
+    SourcesContent: 'sources-content';
+}> = Utils.makeEnum({
+    Sources: 'sources',
+    SourcesContent: 'sources-content'
+});

+ 0 - 25
src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeData.ts

@@ -1,25 +0,0 @@
-import { TNodeWithLexicalScopeStatements } from '../../../types/node/TNodeWithLexicalScopeStatements';
-
-import { IStringArrayScopeCallsWrapperParameterIndexesData } from './IStringArrayScopeCallsWrapperParameterIndexesData';
-
-export interface IStringArrayScopeCallsWrapperLexicalScopeData {
-    /**
-     * @type {TNodeWithLexicalScopeStatements | null}
-     */
-    parentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null;
-
-    /**
-     * @type {number}
-     */
-    resultShiftedIndex: number;
-
-    /**
-     * @type {number}
-     */
-    scopeShiftedIndex: number;
-
-    /**
-     * @type {IStringArrayScopeCallsWrapperParameterIndexesData | null}
-     */
-    callsWrappersParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null;
-}

+ 4 - 2
src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperNamesData.ts → src/interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrappersData.ts

@@ -1,6 +1,8 @@
 import { TStringArrayEncoding } from '../../../types/options/TStringArrayEncoding';
 import { TStringArrayEncoding } from '../../../types/options/TStringArrayEncoding';
 
 
-export interface IStringArrayScopeCallsWrapperNamesData {
+import { IStringArrayScopeCallsWrapperData } from './IStringArrayScopeCallsWrapperData';
+
+export interface IStringArrayScopeCallsWrappersData {
     /**
     /**
      * @type {TStringArrayEncoding}
      * @type {TStringArrayEncoding}
      */
      */
@@ -9,5 +11,5 @@ export interface IStringArrayScopeCallsWrapperNamesData {
     /**
     /**
      * @type {string[]}
      * @type {string[]}
      */
      */
-    names: string[];
+    scopeCallsWrappersData: IStringArrayScopeCallsWrapperData[];
 }
 }

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

@@ -9,6 +9,7 @@ import { TTypeFromEnum } from '../../types/utils/TTypeFromEnum';
 import { IdentifierNamesGenerator } from '../../enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { IdentifierNamesGenerator } from '../../enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
+import { SourceMapSourcesMode } from '../../enums/source-map/SourceMapSourcesMode';
 
 
 export interface IOptions {
 export interface IOptions {
     readonly compact: boolean;
     readonly compact: boolean;
@@ -45,6 +46,7 @@ export interface IOptions {
     readonly sourceMapBaseUrl: string;
     readonly sourceMapBaseUrl: string;
     readonly sourceMapFileName: string;
     readonly sourceMapFileName: string;
     readonly sourceMapMode: TTypeFromEnum<typeof SourceMapMode>;
     readonly sourceMapMode: TTypeFromEnum<typeof SourceMapMode>;
+    readonly sourceMapSourcesMode: TTypeFromEnum<typeof SourceMapSourcesMode>;
     readonly splitStrings: boolean;
     readonly splitStrings: boolean;
     readonly splitStringsChunkLength: number;
     readonly splitStringsChunkLength: number;
     readonly stringArray: boolean;
     readonly stringArray: boolean;

+ 16 - 0
src/interfaces/source-code/ISourceMap.ts

@@ -0,0 +1,16 @@
+export interface ISourceMap {
+    /**
+     * @type {string}
+     */
+    mappings: string;
+
+    /**
+     * @type {string[]}
+     */
+    sources: string[];
+
+    /**
+     * @type {string[]}
+     */
+    sourcesContent: string[];
+}

+ 0 - 10
src/interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeDataStorage.ts

@@ -1,10 +0,0 @@
-import { TNodeWithLexicalScopeStatements } from '../../../types/node/TNodeWithLexicalScopeStatements';
-
-import { IMapStorage } from '../IMapStorage';
-import { IStringArrayScopeCallsWrapperLexicalScopeData } from '../../node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeData';
-
-// eslint-disable-next-line @typescript-eslint/no-empty-interface
-export interface IStringArrayScopeCallsWrapperLexicalScopeDataStorage extends IMapStorage<
-    TNodeWithLexicalScopeStatements,
-    IStringArrayScopeCallsWrapperLexicalScopeData
-> {}

+ 0 - 10
src/interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperNamesDataStorage.ts

@@ -1,10 +0,0 @@
-import { TNodeWithLexicalScopeStatements } from '../../../types/node/TNodeWithLexicalScopeStatements';
-import { TStringArrayScopeCallsWrapperNamesDataByEncoding } from '../../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding';
-
-import { IMapStorage } from '../IMapStorage';
-
-// eslint-disable-next-line @typescript-eslint/no-empty-interface
-export interface IStringArrayScopeCallsWrapperNamesDataStorage extends IMapStorage<
-    TNodeWithLexicalScopeStatements,
-    TStringArrayScopeCallsWrapperNamesDataByEncoding
-> {}

+ 10 - 0
src/interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrappersDataStorage.ts

@@ -0,0 +1,10 @@
+import { TNodeWithLexicalScopeStatements } from '../../../types/node/TNodeWithLexicalScopeStatements';
+import { TStringArrayScopeCallsWrappersDataByEncoding } from '../../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrappersDataByEncoding';
+
+import { IMapStorage } from '../IMapStorage';
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface IStringArrayScopeCallsWrappersDataStorage extends IMapStorage<
+    TNodeWithLexicalScopeStatements,
+    TStringArrayScopeCallsWrappersDataByEncoding
+> {}

+ 28 - 0
src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts

@@ -72,6 +72,9 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
                 objectExpressionNode,
                 objectExpressionNode,
                 objectExpressionParentNode
                 objectExpressionParentNode
             )
             )
+            || ObjectExpressionKeysTransformer.isObjectExpressionWithCallExpression(
+                objectExpressionNode,
+            )
             || ObjectExpressionKeysTransformer.isProhibitedSequenceExpression(
             || ObjectExpressionKeysTransformer.isProhibitedSequenceExpression(
                 objectExpressionNode,
                 objectExpressionNode,
                 objectExpressionHostStatement
                 objectExpressionHostStatement
@@ -154,6 +157,31 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
             && objectExpressionNodeParentNode.body === objectExpressionNode;
             && objectExpressionNodeParentNode.body === objectExpressionNode;
     }
     }
 
 
+    /**
+     * @param {ObjectExpression} objectExpressionNode
+     * @returns {boolean}
+     */
+    private static isObjectExpressionWithCallExpression (objectExpressionNode: ESTree.ObjectExpression): boolean {
+        let isCallExpressionLikeNodeFound: boolean = false;
+
+        estraverse.traverse(objectExpressionNode, {
+            enter: (node: ESTree.Node): void | estraverse.VisitorOption => {
+                const isCallExpressionLikeNode = NodeGuards.isCallExpressionNode(node)
+                    || NodeGuards.isNewExpressionNode(node);
+
+                if (!isCallExpressionLikeNode) {
+                    return;
+                }
+
+                isCallExpressionLikeNodeFound = true;
+
+                return estraverse.VisitorOption.Break;
+            }
+        });
+
+        return isCallExpressionLikeNodeFound;
+    }
+
     /**
     /**
      * @param {ObjectExpression} objectExpressionNode
      * @param {ObjectExpression} objectExpressionNode
      * @param {Node} objectExpressionHostNode
      * @param {Node} objectExpressionHostNode

+ 2 - 0
src/node-transformers/rename-properties-transformers/replacer/RenamePropertiesReplacer.ts

@@ -106,6 +106,8 @@ export class RenamePropertiesReplacer implements IRenamePropertiesReplacer {
      */
      */
     private replacePropertyName (propertyName: string): string {
     private replacePropertyName (propertyName: string): string {
         if (this.isReservedName(propertyName)) {
         if (this.isReservedName(propertyName)) {
+            this.identifierNamesGenerator.preserveName(propertyName);
+
             return propertyName;
             return propertyName;
         }
         }
 
 

+ 91 - 132
src/node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer.ts

@@ -6,18 +6,15 @@ import * as ESTree from 'estree';
 import { TInitialData } from '../../types/TInitialData';
 import { TInitialData } from '../../types/TInitialData';
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 import { TStatement } from '../../types/node/TStatement';
 import { TStatement } from '../../types/node/TStatement';
-import { TStringArrayScopeCallsWrapperNamesDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding';
+import { TStringArrayScopeCallsWrappersDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrappersDataByEncoding';
 import { TStringArrayCustomNodeFactory } from '../../types/container/custom-nodes/TStringArrayCustomNodeFactory';
 import { TStringArrayCustomNodeFactory } from '../../types/container/custom-nodes/TStringArrayCustomNodeFactory';
 
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
 import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
-import { IStringArrayScopeCallsWrapperLexicalScopeData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeData';
-import { IStringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeDataStorage';
-import { IStringArrayScopeCallsWrapperNamesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperNamesData';
-import { IStringArrayScopeCallsWrapperNamesDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperNamesDataStorage';
-import { IStringArrayScopeCallsWrapperParameterIndexesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData';
+import { IStringArrayScopeCallsWrappersData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrappersData';
+import { IStringArrayScopeCallsWrappersDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrappersDataStorage';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IVisitedLexicalScopeNodesStackStorage } from '../../interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage';
 import { IVisitedLexicalScopeNodesStackStorage } from '../../interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
@@ -48,14 +45,9 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     private readonly stringArrayStorage: IStringArrayStorage;
     private readonly stringArrayStorage: IStringArrayStorage;
 
 
     /**
     /**
-     * @type {IStringArrayScopeCallsWrapperLexicalScopeDataStorage}
+     * @type {IStringArrayScopeCallsWrappersDataStorage}
      */
      */
-    private readonly stringArrayScopeCallsWrapperLexicalScopeDataStorage: IStringArrayScopeCallsWrapperLexicalScopeDataStorage;
-
-    /**
-     * @type {IStringArrayScopeCallsWrapperNamesDataStorage}
-     */
-    private readonly stringArrayScopeCallsWrapperNamesDataStorage: IStringArrayScopeCallsWrapperNamesDataStorage;
+    private readonly stringArrayScopeCallsWrappersDataStorage: IStringArrayScopeCallsWrappersDataStorage;
 
 
     /**
     /**
      * @type {TStringArrayCustomNodeFactory}
      * @type {TStringArrayCustomNodeFactory}
@@ -72,8 +64,7 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
      * @param {IOptions} options
      * @param {IOptions} options
      * @param {IVisitedLexicalScopeNodesStackStorage} visitedLexicalScopeNodesStackStorage
      * @param {IVisitedLexicalScopeNodesStackStorage} visitedLexicalScopeNodesStackStorage
      * @param {IStringArrayStorage} stringArrayStorage
      * @param {IStringArrayStorage} stringArrayStorage
-     * @param {IStringArrayScopeCallsWrapperNamesDataStorage} stringArrayScopeCallsWrapperNamesDataStorage
-     * @param {IStringArrayScopeCallsWrapperLexicalScopeDataStorage} stringArrayScopeCallsWrapperLexicalScopeDataStorage
+     * @param {IStringArrayScopeCallsWrappersDataStorage} stringArrayScopeCallsWrappersDataStorage
      * @param {TStringArrayCustomNodeFactory} stringArrayTransformerCustomNodeFactory
      * @param {TStringArrayCustomNodeFactory} stringArrayTransformerCustomNodeFactory
      */
      */
     public constructor (
     public constructor (
@@ -81,8 +72,7 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
         @inject(ServiceIdentifiers.IOptions) options: IOptions,
         @inject(ServiceIdentifiers.IOptions) options: IOptions,
         @inject(ServiceIdentifiers.IVisitedLexicalScopeNodesStackStorage) visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage,
         @inject(ServiceIdentifiers.IVisitedLexicalScopeNodesStackStorage) visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage,
         @inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
         @inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
-        @inject(ServiceIdentifiers.IStringArrayScopeCallsWrapperNamesDataStorage) stringArrayScopeCallsWrapperNamesDataStorage: IStringArrayScopeCallsWrapperNamesDataStorage,
-        @inject(ServiceIdentifiers.IStringArrayScopeCallsWrapperLexicalScopeDataStorage) stringArrayScopeCallsWrapperLexicalScopeDataStorage: IStringArrayScopeCallsWrapperLexicalScopeDataStorage,
+        @inject(ServiceIdentifiers.IStringArrayScopeCallsWrappersDataStorage) stringArrayScopeCallsWrappersDataStorage: IStringArrayScopeCallsWrappersDataStorage,
         @inject(ServiceIdentifiers.Factory__IStringArrayCustomNode)
         @inject(ServiceIdentifiers.Factory__IStringArrayCustomNode)
             stringArrayTransformerCustomNodeFactory: TStringArrayCustomNodeFactory
             stringArrayTransformerCustomNodeFactory: TStringArrayCustomNodeFactory
     ) {
     ) {
@@ -90,8 +80,7 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
 
 
         this.visitedLexicalScopeNodesStackStorage = visitedLexicalScopeNodesStackStorage;
         this.visitedLexicalScopeNodesStackStorage = visitedLexicalScopeNodesStackStorage;
         this.stringArrayStorage = stringArrayStorage;
         this.stringArrayStorage = stringArrayStorage;
-        this.stringArrayScopeCallsWrapperNamesDataStorage = stringArrayScopeCallsWrapperNamesDataStorage;
-        this.stringArrayScopeCallsWrapperLexicalScopeDataStorage = stringArrayScopeCallsWrapperLexicalScopeDataStorage;
+        this.stringArrayScopeCallsWrappersDataStorage = stringArrayScopeCallsWrappersDataStorage;
         this.stringArrayTransformerCustomNodeFactory = stringArrayTransformerCustomNodeFactory;
         this.stringArrayTransformerCustomNodeFactory = stringArrayTransformerCustomNodeFactory;
     }
     }
 
 
@@ -133,56 +122,38 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     public transformNode (
     public transformNode (
         lexicalScopeBodyNode: TNodeWithLexicalScopeStatements
         lexicalScopeBodyNode: TNodeWithLexicalScopeStatements
     ): TNodeWithLexicalScopeStatements {
     ): TNodeWithLexicalScopeStatements {
-        const stringArrayScopeCallsWrapperNamesDataByEncoding: TStringArrayScopeCallsWrapperNamesDataByEncoding | null =
-            this.stringArrayScopeCallsWrapperNamesDataStorage.get(lexicalScopeBodyNode) ?? null;
-        const stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData | null =
-            this.stringArrayScopeCallsWrapperLexicalScopeDataStorage.get(lexicalScopeBodyNode) ?? null;
+        const stringArrayScopeCallsWrappersDataByEncoding: TStringArrayScopeCallsWrappersDataByEncoding | null =
+            this.stringArrayScopeCallsWrappersDataStorage.get(lexicalScopeBodyNode) ?? null;
 
 
-        if (!stringArrayScopeCallsWrapperNamesDataByEncoding || !stringArrayScopeCallsWrapperLexicalScopeData) {
+        if (!stringArrayScopeCallsWrappersDataByEncoding) {
             return lexicalScopeBodyNode;
             return lexicalScopeBodyNode;
         }
         }
 
 
-        const {
-            callsWrappersParameterIndexesData: stringArrayScopeCallsWrapperParameterIndexes
-        } = stringArrayScopeCallsWrapperLexicalScopeData;
-        const stringArrayScopeCallsWrapperNamesDataList: (IStringArrayScopeCallsWrapperNamesData | undefined)[] =
-            Object.values(stringArrayScopeCallsWrapperNamesDataByEncoding);
+        const stringArrayScopeCallsWrappersDataList: (IStringArrayScopeCallsWrappersData | undefined)[] =
+            Object.values(stringArrayScopeCallsWrappersDataByEncoding);
 
 
         // iterates over data for each encoding type
         // iterates over data for each encoding type
-        for (const stringArrayScopeCallsWrapperNamesData of stringArrayScopeCallsWrapperNamesDataList) {
-            if (!stringArrayScopeCallsWrapperNamesData) {
+        for (const stringArrayScopeCallsWrappersData of stringArrayScopeCallsWrappersDataList) {
+            if (!stringArrayScopeCallsWrappersData) {
                 continue;
                 continue;
             }
             }
 
 
-            const {names} = stringArrayScopeCallsWrapperNamesData;
-            const namesLength: number = names.length;
+            const {scopeCallsWrappersData} = stringArrayScopeCallsWrappersData;
+            const scopeCallsWrappersDataLength: number = scopeCallsWrappersData.length;
 
 
             /**
             /**
              * Iterates over each name of scope wrapper name
              * Iterates over each name of scope wrapper name
              * Reverse iteration appends wrappers at index `0` at the correct order
              * Reverse iteration appends wrappers at index `0` at the correct order
              */
              */
-            for (let i = namesLength - 1; i >= 0; i--) {
-                const stringArrayScopeCallsWrapperName: string = names[i];
-                const {
-                    name: upperStringArrayCallsWrapperName,
-                    index: upperStringArrayCallsWrapperShiftedIndex,
-                    parameterIndexesData: upperStringArrayCallsWrapperParameterIndexes
-                } = this.getUpperStringArrayCallsWrapperData(
-                    stringArrayScopeCallsWrapperNamesData,
-                    stringArrayScopeCallsWrapperLexicalScopeData,
-                );
+            for (let i = scopeCallsWrappersDataLength - 1; i >= 0; i--) {
+                const stringArrayScopeCallsWrapperData = scopeCallsWrappersData[i];
+                const upperStringArrayCallsWrapperData =
+                    this.getUpperStringArrayCallsWrapperData(stringArrayScopeCallsWrappersData);
 
 
-                const stringArrayScopeCallsWrapperNode: TStatement[] = this.getStringArrayScopeCallsWrapperNode(
-                    stringArrayScopeCallsWrapperName,
-                    stringArrayScopeCallsWrapperParameterIndexes,
-                    upperStringArrayCallsWrapperName,
-                    upperStringArrayCallsWrapperParameterIndexes,
-                    upperStringArrayCallsWrapperShiftedIndex
-                );
-
-                NodeAppender.prepend(
+                this.getAndAppendStringArrayScopeCallsWrapperNode(
                     lexicalScopeBodyNode,
                     lexicalScopeBodyNode,
-                    stringArrayScopeCallsWrapperNode
+                    stringArrayScopeCallsWrapperData,
+                    upperStringArrayCallsWrapperData
                 );
                 );
             }
             }
         }
         }
@@ -191,40 +162,32 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     }
     }
 
 
     /**
     /**
-     * @param {IStringArrayScopeCallsWrapperNamesData} stringArrayScopeCallsWrapperNamesData
-     * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperLexicalScopeData
+     * @param {IStringArrayScopeCallsWrappersData} stringArrayScopeCallsWrappersData
      * @returns {IStringArrayScopeCallsWrapperData}
      * @returns {IStringArrayScopeCallsWrapperData}
      */
      */
     private getRootStringArrayCallsWrapperData (
     private getRootStringArrayCallsWrapperData (
-        stringArrayScopeCallsWrapperNamesData: IStringArrayScopeCallsWrapperNamesData,
-        stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
+        stringArrayScopeCallsWrappersData: IStringArrayScopeCallsWrappersData,
     ): IStringArrayScopeCallsWrapperData {
     ): IStringArrayScopeCallsWrapperData {
-        const {encoding} = stringArrayScopeCallsWrapperNamesData;
-        const {resultShiftedIndex} = stringArrayScopeCallsWrapperLexicalScopeData;
+        const {encoding} = stringArrayScopeCallsWrappersData;
 
 
         return {
         return {
             name: this.stringArrayStorage.getStorageCallsWrapperName(encoding),
             name: this.stringArrayStorage.getStorageCallsWrapperName(encoding),
-            index: resultShiftedIndex,
+            index: 0,
             parameterIndexesData: null
             parameterIndexesData: null
         };
         };
     }
     }
 
 
     /**
     /**
-     * @param {IStringArrayScopeCallsWrapperNamesData} stringArrayScopeCallsWrapperNamesData
-     * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperLexicalScopeData
+     * @param {IStringArrayScopeCallsWrappersData} stringArrayScopeCallsWrappersData
      * @returns {IStringArrayScopeCallsWrapperData}
      * @returns {IStringArrayScopeCallsWrapperData}
      */
      */
     private getUpperStringArrayCallsWrapperData (
     private getUpperStringArrayCallsWrapperData (
-        stringArrayScopeCallsWrapperNamesData: IStringArrayScopeCallsWrapperNamesData,
-        stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
+        stringArrayScopeCallsWrappersData: IStringArrayScopeCallsWrappersData,
     ): IStringArrayScopeCallsWrapperData {
     ): IStringArrayScopeCallsWrapperData {
-        const {encoding} = stringArrayScopeCallsWrapperNamesData;
-        const {scopeShiftedIndex} = stringArrayScopeCallsWrapperLexicalScopeData;
+        const {encoding} = stringArrayScopeCallsWrappersData;
 
 
-        const rootStringArrayCallsWrapperData = this.getRootStringArrayCallsWrapperData(
-            stringArrayScopeCallsWrapperNamesData,
-            stringArrayScopeCallsWrapperLexicalScopeData
-        );
+        const rootStringArrayCallsWrapperData =
+            this.getRootStringArrayCallsWrapperData(stringArrayScopeCallsWrappersData);
 
 
         if (!this.options.stringArrayWrappersChainedCalls) {
         if (!this.options.stringArrayWrappersChainedCalls) {
             return rootStringArrayCallsWrapperData;
             return rootStringArrayCallsWrapperData;
@@ -238,72 +201,77 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
             return rootStringArrayCallsWrapperData;
             return rootStringArrayCallsWrapperData;
         }
         }
 
 
-        const parentLexicalScopeNamesDataByEncoding: TStringArrayScopeCallsWrapperNamesDataByEncoding | null = this.stringArrayScopeCallsWrapperNamesDataStorage
-            .get(parentLexicalScopeBodyNode) ?? null;
-        const parentScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData | null = this.stringArrayScopeCallsWrapperLexicalScopeDataStorage
-            .get(parentLexicalScopeBodyNode) ?? null;
-
-        const parentLexicalScopeNames: string[] | null = parentLexicalScopeNamesDataByEncoding?.[encoding]?.names ?? null;
+        const parentLexicalScopeCallsWrappersDataByEncoding: TStringArrayScopeCallsWrappersDataByEncoding | null =
+            this.stringArrayScopeCallsWrappersDataStorage
+                .get(parentLexicalScopeBodyNode) ?? null;
+        const parentScopeCallsWrappersData: IStringArrayScopeCallsWrapperData[] | null =
+            parentLexicalScopeCallsWrappersDataByEncoding?.[encoding]?.scopeCallsWrappersData ?? null;
 
 
-        if (!parentLexicalScopeNames?.length) {
+        if (!parentScopeCallsWrappersData?.length) {
             return rootStringArrayCallsWrapperData;
             return rootStringArrayCallsWrapperData;
         }
         }
 
 
-        const upperStringArrayCallsWrapperName: string = this.randomGenerator
+        return this.randomGenerator
             .getRandomGenerator()
             .getRandomGenerator()
-            .pickone(parentLexicalScopeNames);
-        const parameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null =
-            parentScopeCallsWrapperLexicalScopeData?.callsWrappersParameterIndexesData ?? null;
-
-        return {
-            name: upperStringArrayCallsWrapperName,
-            index: scopeShiftedIndex,
-            parameterIndexesData
-        };
+            .pickone(parentScopeCallsWrappersData);
     }
     }
 
 
     /**
     /**
-     * @param {string} stringArrayScopeCallsWrapperName
-     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} stringArrayScopeCallsWrapperParameterIndexes
-     * @param {string} upperStringArrayCallsWrapperName
-     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} upperStringArrayCallsWrapperParameterIndexes
-     * @param {number} stringArrayScopeCallsWrapperShiftedIndex
-     * @returns {TStatement[]}
+     * @param {TNodeWithLexicalScopeStatements} lexicalScopeBodyNode
+     * @param {IStringArrayScopeCallsWrapperData} stringArrayScopeCallsWrapperData
+     * @param {IStringArrayScopeCallsWrapperData} upperStringArrayCallsWrapperData
      */
      */
-    private getStringArrayScopeCallsWrapperNode (
-        stringArrayScopeCallsWrapperName: string,
-        stringArrayScopeCallsWrapperParameterIndexes: IStringArrayScopeCallsWrapperParameterIndexesData | null,
-        upperStringArrayCallsWrapperName: string,
-        upperStringArrayCallsWrapperParameterIndexes: IStringArrayScopeCallsWrapperParameterIndexesData | null,
-        stringArrayScopeCallsWrapperShiftedIndex: number
-    ): TStatement[] {
+    private getAndAppendStringArrayScopeCallsWrapperNode (
+        lexicalScopeBodyNode: TNodeWithLexicalScopeStatements,
+        stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData,
+        upperStringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData,
+    ): void {
+        let stringArrayScopeCallsWrapperNode: TStatement[];
+
         switch (this.options.stringArrayWrappersType) {
         switch (this.options.stringArrayWrappersType) {
-            case StringArrayWrappersType.Function:
-                return this.getStringArrayScopeCallsWrapperFunctionNode(
-                    stringArrayScopeCallsWrapperName,
-                    stringArrayScopeCallsWrapperParameterIndexes,
-                    upperStringArrayCallsWrapperName,
-                    upperStringArrayCallsWrapperParameterIndexes,
-                    stringArrayScopeCallsWrapperShiftedIndex
+            case StringArrayWrappersType.Function: {
+                const randomIndex: number = this.randomGenerator.getRandomInteger(
+                    0,
+                    lexicalScopeBodyNode.body.length - 1
+                );
+
+                stringArrayScopeCallsWrapperNode = this.getStringArrayScopeCallsWrapperFunctionNode(
+                    stringArrayScopeCallsWrapperData,
+                    upperStringArrayCallsWrapperData,
+                );
+
+                NodeAppender.insertAtIndex(
+                    lexicalScopeBodyNode,
+                    stringArrayScopeCallsWrapperNode,
+                    randomIndex
                 );
                 );
 
 
+                break;
+            }
+
             case StringArrayWrappersType.Variable:
             case StringArrayWrappersType.Variable:
-            default:
-                return this.getStringArrayScopeCallsWrapperVariableNode(
-                    stringArrayScopeCallsWrapperName,
-                    upperStringArrayCallsWrapperName
+            default: {
+                stringArrayScopeCallsWrapperNode = this.getStringArrayScopeCallsWrapperVariableNode(
+                    stringArrayScopeCallsWrapperData,
+                    upperStringArrayCallsWrapperData
                 );
                 );
+
+                NodeAppender.prepend(
+                    lexicalScopeBodyNode,
+                    stringArrayScopeCallsWrapperNode
+                );
+            }
         }
         }
     }
     }
 
 
     /**
     /**
-     * @param {string} stringArrayScopeCallsWrapperName
-     * @param {string} upperStringArrayCallsWrapperName
+     * @param {IStringArrayScopeCallsWrapperData} stringArrayScopeCallsWrapperData
+     * @param {IStringArrayScopeCallsWrapperData} upperStringArrayCallsWrapperData
      * @returns {TStatement[]}
      * @returns {TStatement[]}
      */
      */
     private getStringArrayScopeCallsWrapperVariableNode (
     private getStringArrayScopeCallsWrapperVariableNode (
-        stringArrayScopeCallsWrapperName: string,
-        upperStringArrayCallsWrapperName: string
+        stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData,
+        upperStringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData
     ): TStatement[] {
     ): TStatement[] {
         const stringArrayScopeCallsWrapperVariableNode: ICustomNode<TInitialData<StringArrayScopeCallsWrapperVariableNode>> =
         const stringArrayScopeCallsWrapperVariableNode: ICustomNode<TInitialData<StringArrayScopeCallsWrapperVariableNode>> =
             this.stringArrayTransformerCustomNodeFactory(
             this.stringArrayTransformerCustomNodeFactory(
@@ -311,27 +279,21 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
             );
             );
 
 
         stringArrayScopeCallsWrapperVariableNode.initialize(
         stringArrayScopeCallsWrapperVariableNode.initialize(
-            stringArrayScopeCallsWrapperName,
-            upperStringArrayCallsWrapperName
+            stringArrayScopeCallsWrapperData,
+            upperStringArrayCallsWrapperData
         );
         );
 
 
         return stringArrayScopeCallsWrapperVariableNode.getNode();
         return stringArrayScopeCallsWrapperVariableNode.getNode();
     }
     }
 
 
     /**
     /**
-     * @param {string} stringArrayScopeCallsWrapperName
-     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} stringArrayScopeCallsWrapperParameterIndexes
-     * @param {string} upperStringArrayCallsWrapperName
-     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} upperStringArrayCallsWrapperParameterIndexes
-     * @param {number} stringArrayScopeCallsWrapperShiftedIndex
+     * @param {IStringArrayScopeCallsWrapperData} stringArrayScopeCallsWrapperData
+     * @param {IStringArrayScopeCallsWrapperData} upperStringArrayCallsWrapperData
      * @returns {TStatement[]}
      * @returns {TStatement[]}
      */
      */
     private getStringArrayScopeCallsWrapperFunctionNode (
     private getStringArrayScopeCallsWrapperFunctionNode (
-        stringArrayScopeCallsWrapperName: string,
-        stringArrayScopeCallsWrapperParameterIndexes: IStringArrayScopeCallsWrapperParameterIndexesData | null,
-        upperStringArrayCallsWrapperName: string,
-        upperStringArrayCallsWrapperParameterIndexes: IStringArrayScopeCallsWrapperParameterIndexesData | null,
-        stringArrayScopeCallsWrapperShiftedIndex: number
+        stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData,
+        upperStringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData,
     ): TStatement[] {
     ): TStatement[] {
         const stringArrayScopeCallsWrapperFunctionNode: ICustomNode<TInitialData<StringArrayScopeCallsWrapperFunctionNode>> =
         const stringArrayScopeCallsWrapperFunctionNode: ICustomNode<TInitialData<StringArrayScopeCallsWrapperFunctionNode>> =
             this.stringArrayTransformerCustomNodeFactory(
             this.stringArrayTransformerCustomNodeFactory(
@@ -339,11 +301,8 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
             );
             );
 
 
         stringArrayScopeCallsWrapperFunctionNode.initialize(
         stringArrayScopeCallsWrapperFunctionNode.initialize(
-            stringArrayScopeCallsWrapperName,
-            stringArrayScopeCallsWrapperParameterIndexes,
-            upperStringArrayCallsWrapperName,
-            upperStringArrayCallsWrapperParameterIndexes,
-            stringArrayScopeCallsWrapperShiftedIndex,
+            stringArrayScopeCallsWrapperData,
+            upperStringArrayCallsWrapperData
         );
         );
 
 
         return stringArrayScopeCallsWrapperFunctionNode.getNode();
         return stringArrayScopeCallsWrapperFunctionNode.getNode();

+ 63 - 119
src/node-transformers/string-array-transformers/StringArrayTransformer.ts

@@ -7,7 +7,7 @@ import { TIdentifierNamesGeneratorFactory } from '../../types/container/generato
 import { TInitialData } from '../../types/TInitialData';
 import { TInitialData } from '../../types/TInitialData';
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 import { TStatement } from '../../types/node/TStatement';
 import { TStatement } from '../../types/node/TStatement';
-import { TStringArrayScopeCallsWrapperNamesDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding';
+import { TStringArrayScopeCallsWrappersDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrappersDataByEncoding';
 import { TStringArrayCustomNodeFactory } from '../../types/container/custom-nodes/TStringArrayCustomNodeFactory';
 import { TStringArrayCustomNodeFactory } from '../../types/container/custom-nodes/TStringArrayCustomNodeFactory';
 
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
@@ -16,9 +16,7 @@ import { ILiteralNodesCacheStorage } from '../../interfaces/storages/string-arra
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
 import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
-import { IStringArrayScopeCallsWrapperLexicalScopeData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeData';
-import { IStringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeDataStorage';
-import { IStringArrayScopeCallsWrapperNamesDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperNamesDataStorage';
+import { IStringArrayScopeCallsWrappersDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrappersDataStorage';
 import { IStringArrayScopeCallsWrapperParameterIndexesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData';
 import { IStringArrayScopeCallsWrapperParameterIndexesData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperParameterIndexesData';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
 import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
@@ -57,7 +55,6 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
         NodeTransformer.StringArrayRotateFunctionTransformer
         NodeTransformer.StringArrayRotateFunctionTransformer
     ];
     ];
 
 
-
     /**
     /**
      * @type {IIdentifierNamesGenerator}
      * @type {IIdentifierNamesGenerator}
      */
      */
@@ -79,14 +76,9 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
     private readonly stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer;
     private readonly stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer;
 
 
     /**
     /**
-     * @type {IStringArrayScopeCallsWrapperLexicalScopeDataStorage}
-     */
-    private readonly stringArrayScopeCallsWrapperLexicalScopeDataStorage: IStringArrayScopeCallsWrapperLexicalScopeDataStorage;
-
-    /**
-     * @type {IStringArrayScopeCallsWrapperNamesDataStorage}
+     * @type {IStringArrayScopeCallsWrappersDataStorage}
      */
      */
-    private readonly stringArrayScopeCallsWrapperNamesDataStorage: IStringArrayScopeCallsWrapperNamesDataStorage;
+    private readonly stringArrayScopeCallsWrappersDataStorage: IStringArrayScopeCallsWrappersDataStorage;
 
 
     /**
     /**
      * @type {TStringArrayCustomNodeFactory}
      * @type {TStringArrayCustomNodeFactory}
@@ -104,8 +96,7 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
      * @param {ILiteralNodesCacheStorage} literalNodesCacheStorage
      * @param {ILiteralNodesCacheStorage} literalNodesCacheStorage
      * @param {IVisitedLexicalScopeNodesStackStorage} visitedLexicalScopeNodesStackStorage
      * @param {IVisitedLexicalScopeNodesStackStorage} visitedLexicalScopeNodesStackStorage
      * @param {IStringArrayStorage} stringArrayStorage
      * @param {IStringArrayStorage} stringArrayStorage
-     * @param {IStringArrayScopeCallsWrapperNamesDataStorage} stringArrayScopeCallsWrapperNamesDataStorage
-     * @param {IStringArrayScopeCallsWrapperLexicalScopeDataStorage} stringArrayScopeCallsWrapperLexicalScopeDataStorage
+     * @param {IStringArrayScopeCallsWrappersDataStorage} stringArrayScopeCallsWrappersDataStorage
      * @param {IStringArrayStorageAnalyzer} stringArrayStorageAnalyzer
      * @param {IStringArrayStorageAnalyzer} stringArrayStorageAnalyzer
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TStringArrayCustomNodeFactory} stringArrayTransformerCustomNodeFactory
      * @param {TStringArrayCustomNodeFactory} stringArrayTransformerCustomNodeFactory
@@ -116,10 +107,8 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
         @inject(ServiceIdentifiers.ILiteralNodesCacheStorage) literalNodesCacheStorage: ILiteralNodesCacheStorage,
         @inject(ServiceIdentifiers.ILiteralNodesCacheStorage) literalNodesCacheStorage: ILiteralNodesCacheStorage,
         @inject(ServiceIdentifiers.IVisitedLexicalScopeNodesStackStorage) visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage,
         @inject(ServiceIdentifiers.IVisitedLexicalScopeNodesStackStorage) visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage,
         @inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
         @inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
-        @inject(ServiceIdentifiers.IStringArrayScopeCallsWrapperNamesDataStorage)
-            stringArrayScopeCallsWrapperNamesDataStorage: IStringArrayScopeCallsWrapperNamesDataStorage,
-        @inject(ServiceIdentifiers.IStringArrayScopeCallsWrapperLexicalScopeDataStorage)
-            stringArrayScopeCallsWrapperLexicalScopeDataStorage: IStringArrayScopeCallsWrapperLexicalScopeDataStorage,
+        @inject(ServiceIdentifiers.IStringArrayScopeCallsWrappersDataStorage)
+            stringArrayScopeCallsWrappersDataStorage: IStringArrayScopeCallsWrappersDataStorage,
         @inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer,
         @inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer,
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
@@ -131,8 +120,7 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
         this.literalNodesCacheStorage = literalNodesCacheStorage;
         this.literalNodesCacheStorage = literalNodesCacheStorage;
         this.visitedLexicalScopeNodesStackStorage = visitedLexicalScopeNodesStackStorage;
         this.visitedLexicalScopeNodesStackStorage = visitedLexicalScopeNodesStackStorage;
         this.stringArrayStorage = stringArrayStorage;
         this.stringArrayStorage = stringArrayStorage;
-        this.stringArrayScopeCallsWrapperNamesDataStorage = stringArrayScopeCallsWrapperNamesDataStorage;
-        this.stringArrayScopeCallsWrapperLexicalScopeDataStorage = stringArrayScopeCallsWrapperLexicalScopeDataStorage;
+        this.stringArrayScopeCallsWrappersDataStorage = stringArrayScopeCallsWrappersDataStorage;
         this.stringArrayStorageAnalyzer = stringArrayStorageAnalyzer;
         this.stringArrayStorageAnalyzer = stringArrayStorageAnalyzer;
         this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
         this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
         this.stringArrayTransformerCustomNodeFactory = stringArrayTransformerCustomNodeFactory;
         this.stringArrayTransformerCustomNodeFactory = stringArrayTransformerCustomNodeFactory;
@@ -219,21 +207,17 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
      * @returns {Expression}
      * @returns {Expression}
      */
      */
     private getStringArrayCallNode (stringArrayStorageItemData: IStringArrayStorageItemData): ESTree.Expression {
     private getStringArrayCallNode (stringArrayStorageItemData: IStringArrayStorageItemData): ESTree.Expression {
-        const {
-            name: stringArrayCallsWrapperName,
-            index,
-            parameterIndexesData
-        } = this.getStringArrayCallsWrapperData(stringArrayStorageItemData);
-        const {decodeKey } = stringArrayStorageItemData;
+        const stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData =
+            this.getStringArrayScopeCallsWrapperData(stringArrayStorageItemData);
+        const {decodeKey, index} = stringArrayStorageItemData;
 
 
         const stringArrayCallCustomNode: ICustomNode<TInitialData<StringArrayCallNode>> =
         const stringArrayCallCustomNode: ICustomNode<TInitialData<StringArrayCallNode>> =
             this.stringArrayTransformerCustomNodeFactory(StringArrayCustomNode.StringArrayCallNode);
             this.stringArrayTransformerCustomNodeFactory(StringArrayCustomNode.StringArrayCallNode);
 
 
         stringArrayCallCustomNode.initialize(
         stringArrayCallCustomNode.initialize(
-            stringArrayCallsWrapperName,
-            parameterIndexesData,
             index,
             index,
             this.stringArrayStorage.getIndexShiftAmount(),
             this.stringArrayStorage.getIndexShiftAmount(),
+            stringArrayScopeCallsWrapperData,
             decodeKey
             decodeKey
         );
         );
 
 
@@ -250,29 +234,29 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
      * @returns {IStringArrayScopeCallsWrapperData}
      * @returns {IStringArrayScopeCallsWrapperData}
      */
      */
-    private getStringArrayCallsWrapperData (
+    private getStringArrayScopeCallsWrapperData (
         stringArrayStorageItemData: IStringArrayStorageItemData
         stringArrayStorageItemData: IStringArrayStorageItemData
     ): IStringArrayScopeCallsWrapperData {
     ): IStringArrayScopeCallsWrapperData {
         return !this.options.stringArrayWrappersCount
         return !this.options.stringArrayWrappersCount
-            ? this.getRootStringArrayCallsWrapperData(stringArrayStorageItemData)
-            : this.getUpperStringArrayCallsWrapperData(stringArrayStorageItemData);
+            ? this.getRootStringArrayScopeCallsWrapperData(stringArrayStorageItemData)
+            : this.getUpperStringArrayScopeCallsWrapperData(stringArrayStorageItemData);
     }
     }
 
 
     /**
     /**
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
      * @returns {IStringArrayScopeCallsWrapperData}
      * @returns {IStringArrayScopeCallsWrapperData}
      */
      */
-    private getRootStringArrayCallsWrapperData (
+    private getRootStringArrayScopeCallsWrapperData (
         stringArrayStorageItemData: IStringArrayStorageItemData
         stringArrayStorageItemData: IStringArrayStorageItemData
     ): IStringArrayScopeCallsWrapperData {
     ): IStringArrayScopeCallsWrapperData {
-        const {encoding, index} = stringArrayStorageItemData;
+        const {encoding} = stringArrayStorageItemData;
 
 
         const rootStringArrayCallsWrapperName: string = this.stringArrayStorage.getStorageCallsWrapperName(encoding);
         const rootStringArrayCallsWrapperName: string = this.stringArrayStorage.getStorageCallsWrapperName(encoding);
 
 
         return {
         return {
             name: rootStringArrayCallsWrapperName,
             name: rootStringArrayCallsWrapperName,
-            parameterIndexesData: null,
-            index
+            index: 0,
+            parameterIndexesData: null
         };
         };
     }
     }
 
 
@@ -280,143 +264,103 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
      * @returns {IStringArrayScopeCallsWrapperData}
      * @returns {IStringArrayScopeCallsWrapperData}
      */
      */
-    private getUpperStringArrayCallsWrapperData (
+    private getUpperStringArrayScopeCallsWrapperData (
         stringArrayStorageItemData: IStringArrayStorageItemData
         stringArrayStorageItemData: IStringArrayStorageItemData
     ): IStringArrayScopeCallsWrapperData {
     ): IStringArrayScopeCallsWrapperData {
-        const {encoding, index} = stringArrayStorageItemData;
+        const {encoding} = stringArrayStorageItemData;
         const currentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null =
         const currentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null =
             this.visitedLexicalScopeNodesStackStorage.getLastElement() ?? null;
             this.visitedLexicalScopeNodesStackStorage.getLastElement() ?? null;
-        const parentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null =
-            this.visitedLexicalScopeNodesStackStorage.getPenultimateElement() ?? null;
 
 
         if (!currentLexicalScopeBodyNode) {
         if (!currentLexicalScopeBodyNode) {
             throw new Error('Cannot find current lexical scope body node');
             throw new Error('Cannot find current lexical scope body node');
         }
         }
 
 
-        const stringArrayScopeCallsWrapperNamesDataByEncoding: TStringArrayScopeCallsWrapperNamesDataByEncoding =
-            this.getAndUpdateStringArrayScopeCallsWrapperNamesDataByEncoding(
+        const stringArrayScopeCallsWrappersDataByEncoding: TStringArrayScopeCallsWrappersDataByEncoding =
+            this.getAndUpdateStringArrayScopeCallsWrappersDataByEncoding(
                 currentLexicalScopeBodyNode,
                 currentLexicalScopeBodyNode,
                 stringArrayStorageItemData
                 stringArrayStorageItemData
             );
             );
-        const stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData =
-            this.getAndUpdateStringArrayScopeCallsWrapperLexicalScopeData(
-                currentLexicalScopeBodyNode,
-                parentLexicalScopeBodyNode
-            );
-
-        const stringArrayScopeCallsWrapperNames: string[] = stringArrayScopeCallsWrapperNamesDataByEncoding[encoding]?.names ?? [];
-        const randomUpperStringArrayCallsWrapperName: string = this.randomGenerator
-            .getRandomGenerator()
-            .pickone(stringArrayScopeCallsWrapperNames);
 
 
-        const resultIndex: number = stringArrayScopeCallsWrapperLexicalScopeData
-            ? stringArrayScopeCallsWrapperLexicalScopeData.resultShiftedIndex + index
-            : index;
+        const stringArrayScopeCallsWrappersData: IStringArrayScopeCallsWrapperData[] =
+            stringArrayScopeCallsWrappersDataByEncoding[encoding]?.scopeCallsWrappersData ?? [];
 
 
-        return {
-            name: randomUpperStringArrayCallsWrapperName,
-            index: resultIndex,
-            parameterIndexesData: stringArrayScopeCallsWrapperLexicalScopeData.callsWrappersParameterIndexesData
-        };
+        return this.randomGenerator
+            .getRandomGenerator()
+            .pickone(stringArrayScopeCallsWrappersData);
     }
     }
 
 
     /**
     /**
      * @param {TNodeWithLexicalScopeStatements} currentLexicalScopeBodyNode
      * @param {TNodeWithLexicalScopeStatements} currentLexicalScopeBodyNode
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
-     * @returns {TStringArrayScopeCallsWrapperNamesDataByEncoding}
+     * @returns {TStringArrayScopeCallsWrappersDataByEncoding}
      */
      */
-    private getAndUpdateStringArrayScopeCallsWrapperNamesDataByEncoding (
+    private getAndUpdateStringArrayScopeCallsWrappersDataByEncoding (
         currentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements,
         currentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements,
-        stringArrayStorageItemData: IStringArrayStorageItemData
-    ): TStringArrayScopeCallsWrapperNamesDataByEncoding {
+        stringArrayStorageItemData: IStringArrayStorageItemData,
+    ): TStringArrayScopeCallsWrappersDataByEncoding {
         const {encoding} = stringArrayStorageItemData;
         const {encoding} = stringArrayStorageItemData;
-        const stringArrayScopeCallsWrapperNamesDataByEncoding: TStringArrayScopeCallsWrapperNamesDataByEncoding =
-            this.stringArrayScopeCallsWrapperNamesDataStorage.get(currentLexicalScopeBodyNode)
+        const stringArrayScopeCallsWrappersDataByEncoding: TStringArrayScopeCallsWrappersDataByEncoding =
+            this.stringArrayScopeCallsWrappersDataStorage.get(currentLexicalScopeBodyNode)
             ?? {};
             ?? {};
 
 
-        const stringArrayScopeCallsWrapperNames: string[] = stringArrayScopeCallsWrapperNamesDataByEncoding[encoding]?.names ?? [];
-        const isFilledScopeCallsWrapperNamesList: boolean = stringArrayScopeCallsWrapperNames.length === this.options.stringArrayWrappersCount;
+        const stringArrayScopeCallsWrappersData: IStringArrayScopeCallsWrapperData[] =
+            stringArrayScopeCallsWrappersDataByEncoding[encoding]?.scopeCallsWrappersData ?? [];
+        const isFilledScopeCallsWrapperNamesList: boolean =
+            stringArrayScopeCallsWrappersData.length === this.options.stringArrayWrappersCount;
 
 
         if (isFilledScopeCallsWrapperNamesList) {
         if (isFilledScopeCallsWrapperNamesList) {
-            return stringArrayScopeCallsWrapperNamesDataByEncoding;
+            return stringArrayScopeCallsWrappersDataByEncoding;
         }
         }
 
 
         // have to use `generateForGlobalScope` for program node for correct attach prefix to the calls wrapper name
         // have to use `generateForGlobalScope` for program node for correct attach prefix to the calls wrapper name
         const nextScopeCallsWrapperName: string = NodeGuards.isProgramNode(currentLexicalScopeBodyNode)
         const nextScopeCallsWrapperName: string = NodeGuards.isProgramNode(currentLexicalScopeBodyNode)
             ? this.identifierNamesGenerator.generateForGlobalScope()
             ? this.identifierNamesGenerator.generateForGlobalScope()
             : this.identifierNamesGenerator.generateNext();
             : this.identifierNamesGenerator.generateNext();
+        const nextScopeCallsWrapperShiftedIndex: number =
+            this.getStringArrayCallsWrapperShiftedIndex();
+        const nextScopeCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null =
+            this.getStringArrayCallsWrapperParameterIndexesData();
 
 
-        stringArrayScopeCallsWrapperNamesDataByEncoding[encoding] = {
+        stringArrayScopeCallsWrappersDataByEncoding[encoding] = {
             encoding,
             encoding,
-            names: [
-                ...stringArrayScopeCallsWrapperNames,
-                nextScopeCallsWrapperName
+            scopeCallsWrappersData: [
+                ...stringArrayScopeCallsWrappersData,
+                {
+                    name: nextScopeCallsWrapperName,
+                    index: nextScopeCallsWrapperShiftedIndex,
+                    parameterIndexesData: nextScopeCallsWrapperParameterIndexesData
+                }
             ]
             ]
         };
         };
 
 
-        this.stringArrayScopeCallsWrapperNamesDataStorage.set(
+        this.stringArrayScopeCallsWrappersDataStorage.set(
             currentLexicalScopeBodyNode,
             currentLexicalScopeBodyNode,
-            stringArrayScopeCallsWrapperNamesDataByEncoding
+            stringArrayScopeCallsWrappersDataByEncoding
         );
         );
 
 
-        return stringArrayScopeCallsWrapperNamesDataByEncoding;
+        return stringArrayScopeCallsWrappersDataByEncoding;
     }
     }
 
 
     /**
     /**
-     * @param {TNodeWithLexicalScopeStatements} currentLexicalScopeBodyNode
-     * @param {TNodeWithLexicalScopeStatements | null} parentLexicalScopeBodyNode
-     * @returns {IStringArrayScopeCallsWrapperLexicalScopeData}
-     * @private
+     * @returns {number}
      */
      */
-    private getAndUpdateStringArrayScopeCallsWrapperLexicalScopeData (
-        currentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements,
-        parentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null
-    ): IStringArrayScopeCallsWrapperLexicalScopeData {
-        const storedLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData | null =
-            this.stringArrayScopeCallsWrapperLexicalScopeDataStorage.get(currentLexicalScopeBodyNode)
-            ?? null;
-
-        if (storedLexicalScopeData) {
-            return storedLexicalScopeData;
-        }
-
-        const parentLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData | null = parentLexicalScopeBodyNode
-            ? this.stringArrayScopeCallsWrapperLexicalScopeDataStorage.get(parentLexicalScopeBodyNode) ?? null
-            : null;
-
-        const callsWrappersParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null =
-            this.options.stringArrayWrappersType === StringArrayWrappersType.Function
-            ? this.getStringArrayCallsWrapperParameterIndexesData()
-            : null;
-        const scopeShiftedIndex: number = this.options.stringArrayWrappersType === StringArrayWrappersType.Function
+    private getStringArrayCallsWrapperShiftedIndex (): number {
+        return this.options.stringArrayWrappersType === StringArrayWrappersType.Function
             ? this.randomGenerator.getRandomInteger(
             ? this.randomGenerator.getRandomInteger(
                 StringArrayTransformer.minShiftedIndexValue,
                 StringArrayTransformer.minShiftedIndexValue,
                 StringArrayTransformer.maxShiftedIndexValue
                 StringArrayTransformer.maxShiftedIndexValue
             )
             )
             : 0;
             : 0;
-        const resultShiftedIndex: number = parentLexicalScopeData
-            ? parentLexicalScopeData.resultShiftedIndex + scopeShiftedIndex
-            : scopeShiftedIndex;
-
-        const lexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData = {
-            callsWrappersParameterIndexesData,
-            parentLexicalScopeBodyNode,
-            resultShiftedIndex,
-            scopeShiftedIndex
-        };
-
-        this.stringArrayScopeCallsWrapperLexicalScopeDataStorage.set(
-            currentLexicalScopeBodyNode,
-            lexicalScopeData
-        );
-
-        return lexicalScopeData;
     }
     }
 
 
     /**
     /**
-     * @returns {IStringArrayScopeCallsWrapperParameterIndexesData}
+     * @returns {IStringArrayScopeCallsWrapperParameterIndexesData | null}
      */
      */
-    private getStringArrayCallsWrapperParameterIndexesData (): IStringArrayScopeCallsWrapperParameterIndexesData {
+    private getStringArrayCallsWrapperParameterIndexesData (): IStringArrayScopeCallsWrapperParameterIndexesData | null {
+        if (this.options.stringArrayWrappersType !== StringArrayWrappersType.Function) {
+            return null;
+        }
+
         const minIndexValue: number = 0;
         const minIndexValue: number = 0;
         const maxIndexValue: number = this.options.stringArrayWrappersParametersMaxCount - 1;
         const maxIndexValue: number = this.options.stringArrayWrappersParametersMaxCount - 1;
 
 

+ 24 - 0
src/node/NodeGuards.ts

@@ -101,6 +101,14 @@ export class NodeGuards {
         return node.type === NodeType.ClassDeclaration && node.id !== null;
         return node.type === NodeType.ClassDeclaration && node.id !== null;
     }
     }
 
 
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    public static isConditionalExpressionNode (node: ESTree.Node): node is ESTree.ConditionalExpression {
+        return node.type === NodeType.ConditionalExpression;
+    }
+
     /**
     /**
      * @param {Node} node
      * @param {Node} node
      * @returns {boolean}
      * @returns {boolean}
@@ -285,6 +293,14 @@ export class NodeGuards {
         return node.type === NodeType.Literal;
         return node.type === NodeType.Literal;
     }
     }
 
 
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    public static isLogicalExpressionNode (node: ESTree.Node): node is ESTree.LogicalExpression {
+        return node.type === NodeType.LogicalExpression;
+    }
+
     /**
     /**
      * @param {Node} node
      * @param {Node} node
      * @returns {boolean}
      * @returns {boolean}
@@ -301,6 +317,14 @@ export class NodeGuards {
         return node.type === NodeType.MethodDefinition;
         return node.type === NodeType.MethodDefinition;
     }
     }
 
 
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    public static isNewExpressionNode (node: ESTree.Node): node is ESTree.NewExpression {
+        return node.type === NodeType.NewExpression;
+    }
+
     /**
     /**
      * @param {Object} object
      * @param {Object} object
      * @returns {boolean}
      * @returns {boolean}

+ 9 - 1
src/options/Options.ts

@@ -35,6 +35,7 @@ import { ObfuscationTarget } from '../enums/ObfuscationTarget';
 import { OptionsPreset } from '../enums/options/presets/OptionsPreset';
 import { OptionsPreset } from '../enums/options/presets/OptionsPreset';
 import { RenamePropertiesMode } from '../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { RenamePropertiesMode } from '../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { SourceMapMode } from '../enums/source-map/SourceMapMode';
 import { SourceMapMode } from '../enums/source-map/SourceMapMode';
+import { SourceMapSourcesMode } from '../enums/source-map/SourceMapSourcesMode';
 import { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayEncoding } from '../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayEncoding } from '../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayWrappersType } from '../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 import { StringArrayWrappersType } from '../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
@@ -48,6 +49,7 @@ import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
 import { IsAllowedForObfuscationTargets } from './validators/IsAllowedForObfuscationTargets';
 import { IsAllowedForObfuscationTargets } from './validators/IsAllowedForObfuscationTargets';
 import { IsDomainLockRedirectUrl } from './validators/IsDomainLockRedirectUrl';
 import { IsDomainLockRedirectUrl } from './validators/IsDomainLockRedirectUrl';
 import { IsIdentifierNamesCache } from './validators/IsIdentifierNamesCache';
 import { IsIdentifierNamesCache } from './validators/IsIdentifierNamesCache';
+import { IsInputFileName } from './validators/IsInputFileName';
 
 
 @injectable()
 @injectable()
 export class Options implements IOptions {
 export class Options implements IOptions {
@@ -194,7 +196,7 @@ export class Options implements IOptions {
     /**
     /**
      * @type {string}
      * @type {string}
      */
      */
-    @IsString()
+    @IsInputFileName()
     public readonly inputFileName!: string;
     public readonly inputFileName!: string;
 
 
     /**
     /**
@@ -312,6 +314,12 @@ export class Options implements IOptions {
     @IsIn([SourceMapMode.Inline, SourceMapMode.Separate])
     @IsIn([SourceMapMode.Inline, SourceMapMode.Separate])
     public readonly sourceMapMode!: TTypeFromEnum<typeof SourceMapMode>;
     public readonly sourceMapMode!: TTypeFromEnum<typeof SourceMapMode>;
 
 
+    /**
+     * @type {SourceMapSourcesMode}
+     */
+    @IsIn([SourceMapSourcesMode.Sources, SourceMapSourcesMode.SourcesContent])
+    public readonly sourceMapSourcesMode!: TTypeFromEnum<typeof SourceMapSourcesMode>;
+
     /**
     /**
      * @type {boolean}
      * @type {boolean}
      */
      */

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

@@ -5,6 +5,7 @@ import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { OptionsPreset } from '../../enums/options/presets/OptionsPreset';
 import { OptionsPreset } from '../../enums/options/presets/OptionsPreset';
 import { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
+import { SourceMapSourcesMode } from '../../enums/source-map/SourceMapSourcesMode';
 import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayEncoding } from '../../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayEncoding } from '../../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 import { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
@@ -46,6 +47,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     sourceMapBaseUrl: '',
     sourceMapBaseUrl: '',
     sourceMapFileName: '',
     sourceMapFileName: '',
     sourceMapMode: SourceMapMode.Separate,
     sourceMapMode: SourceMapMode.Separate,
+    sourceMapSourcesMode: SourceMapSourcesMode.SourcesContent,
     splitStrings: false,
     splitStrings: false,
     splitStringsChunkLength: 10,
     splitStringsChunkLength: 10,
     stringArray: true,
     stringArray: true,

+ 2 - 0
src/options/presets/NoCustomNodes.ts

@@ -4,6 +4,7 @@ import { IdentifierNamesGenerator } from '../../enums/generators/identifier-name
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
+import { SourceMapSourcesMode } from '../../enums/source-map/SourceMapSourcesMode';
 import { StringArrayEncoding } from '../../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayEncoding } from '../../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 import { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
@@ -42,6 +43,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
     sourceMapBaseUrl: '',
     sourceMapBaseUrl: '',
     sourceMapFileName: '',
     sourceMapFileName: '',
     sourceMapMode: SourceMapMode.Separate,
     sourceMapMode: SourceMapMode.Separate,
+    sourceMapSourcesMode: SourceMapSourcesMode.SourcesContent,
     splitStrings: false,
     splitStrings: false,
     splitStringsChunkLength: 0,
     splitStringsChunkLength: 0,
     stringArray: false,
     stringArray: false,

+ 18 - 0
src/options/validators/IsInputFileName.ts

@@ -0,0 +1,18 @@
+import { IsNotEmpty, IsString, ValidateIf } from 'class-validator';
+
+import { TInputOptions } from '../../types/options/TInputOptions';
+
+import { SourceMapSourcesMode } from '../../enums/source-map/SourceMapSourcesMode';
+
+/**
+ * @returns {PropertyDecorator}
+ */
+export const IsInputFileName = (): PropertyDecorator => {
+    return (target: any, key: string | symbol): void => {
+        IsString()(target, key);
+        ValidateIf(({sourceMapSourcesMode}: TInputOptions) => {
+            return sourceMapSourcesMode === SourceMapSourcesMode.Sources;
+        })(target, key);
+        IsNotEmpty()(target, key);
+    };
+};

+ 0 - 28
src/storages/string-array-transformers/StringArrayScopeCallsWrapperLexicalScopeDataStorage.ts

@@ -1,28 +0,0 @@
-import { inject, injectable } from 'inversify';
-import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
-
-import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
-
-import { IOptions } from '../../interfaces/options/IOptions';
-import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
-import { IStringArrayScopeCallsWrapperLexicalScopeData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeData';
-import { IStringArrayScopeCallsWrapperLexicalScopeDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperLexicalScopeDataStorage';
-
-import { MapStorage } from '../MapStorage';
-
-@injectable()
-export class StringArrayScopeCallsWrapperLexicalScopeDataStorage extends MapStorage <
-    TNodeWithLexicalScopeStatements,
-    IStringArrayScopeCallsWrapperLexicalScopeData
-> implements IStringArrayScopeCallsWrapperLexicalScopeDataStorage {
-    /**
-     * @param {IRandomGenerator} randomGenerator
-     * @param {IOptions} options
-     */
-    public constructor (
-        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions
-    ) {
-        super(randomGenerator, options);
-    }
-}

+ 5 - 5
src/storages/string-array-transformers/StringArrayScopeCallsWrapperNamesDataStorage.ts → src/storages/string-array-transformers/StringArrayScopeCallsWrappersDataStorage.ts

@@ -2,19 +2,19 @@ import { inject, injectable } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
 
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
-import { TStringArrayScopeCallsWrapperNamesDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding';
+import { TStringArrayScopeCallsWrappersDataByEncoding } from '../../types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrappersDataByEncoding';
 
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
-import { IStringArrayScopeCallsWrapperNamesDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrapperNamesDataStorage';
+import { IStringArrayScopeCallsWrappersDataStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrappersDataStorage';
 
 
 import { MapStorage } from '../MapStorage';
 import { MapStorage } from '../MapStorage';
 
 
 @injectable()
 @injectable()
-export class StringArrayScopeCallsWrapperNamesDataStorage extends MapStorage <
+export class StringArrayScopeCallsWrappersDataStorage extends MapStorage <
     TNodeWithLexicalScopeStatements,
     TNodeWithLexicalScopeStatements,
-    TStringArrayScopeCallsWrapperNamesDataByEncoding
-> implements IStringArrayScopeCallsWrapperNamesDataStorage {
+    TStringArrayScopeCallsWrappersDataByEncoding
+> implements IStringArrayScopeCallsWrappersDataStorage {
     /**
     /**
      * @param {IRandomGenerator} randomGenerator
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      * @param {IOptions} options

+ 0 - 7
src/types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrapperNamesDataByEncoding.ts

@@ -1,7 +0,0 @@
-import { TStringArrayEncoding } from '../../options/TStringArrayEncoding';
-
-import { IStringArrayScopeCallsWrapperNamesData } from '../../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperNamesData';
-
-export type TStringArrayScopeCallsWrapperNamesDataByEncoding = Partial<{
-    [key in TStringArrayEncoding]: IStringArrayScopeCallsWrapperNamesData;
-}>;

+ 7 - 0
src/types/node-transformers/string-array-transformers/TStringArrayScopeCallsWrappersDataByEncoding.ts

@@ -0,0 +1,7 @@
+import { TStringArrayEncoding } from '../../options/TStringArrayEncoding';
+
+import { IStringArrayScopeCallsWrappersData } from '../../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrappersData';
+
+export type TStringArrayScopeCallsWrappersDataByEncoding = Partial<{
+    [key in TStringArrayEncoding]: IStringArrayScopeCallsWrappersData;
+}>;

+ 2 - 1
test/declarations/index.d.ts

@@ -1,2 +1,3 @@
 /// <reference path="../../src/declarations/escodegen.d.ts" />
 /// <reference path="../../src/declarations/escodegen.d.ts" />
-/// <reference path="../../src/declarations/ESTree.d.ts" />
+/// <reference path="../../src/declarations/ESTree.d.ts" />
+/// <reference path="./source-map-resolve.d.ts" />

+ 19 - 0
test/declarations/source-map-resolve.d.ts

@@ -0,0 +1,19 @@
+declare module 'source-map-resolve' {
+    interface ExistingRawSourceMap {
+        mappings: string;
+        sources: string[];
+        sourcesContent: string[];
+    }
+
+    export interface ResolvedSources {
+        sourcesResolved: string[];
+        sourcesContent: (string | Error)[];
+    }
+
+    export function resolveSources(
+        map: ExistingRawSourceMap,
+        mapUrl: string,
+        read: (path: string, callback: (error: Error | null, data: Buffer | string) => void) => void,
+        callback: (error: Error | null, result: ResolvedSources) => void,
+    ): void;
+}

+ 384 - 64
test/functional-tests/cli/JavaScriptObfuscatorCLI.spec.ts

@@ -3,12 +3,16 @@ import * as mkdirp from 'mkdirp';
 import * as path from 'path';
 import * as path from 'path';
 import * as rimraf from 'rimraf';
 import * as rimraf from 'rimraf';
 import * as sinon from 'sinon';
 import * as sinon from 'sinon';
+import { resolveSources } from 'source-map-resolve';
 
 
 import { assert } from 'chai';
 import { assert } from 'chai';
 
 
+import { ISourceMap } from '../../../src/interfaces/source-code/ISourceMap';
+
 import { StdoutWriteMock } from '../../mocks/StdoutWriteMock';
 import { StdoutWriteMock } from '../../mocks/StdoutWriteMock';
 
 
 import { JavaScriptObfuscatorCLI } from '../../../src/JavaScriptObfuscatorCLIFacade';
 import { JavaScriptObfuscatorCLI } from '../../../src/JavaScriptObfuscatorCLIFacade';
+import { parseSourceMapFromObfuscatedCode } from '../../helpers/parseSourceMapFromObfuscatedCode';
 
 
 describe('JavaScriptObfuscatorCLI', function (): void {
 describe('JavaScriptObfuscatorCLI', function (): void {
     this.timeout(100000);
     this.timeout(100000);
@@ -530,12 +534,12 @@ describe('JavaScriptObfuscatorCLI', function (): void {
 
 
             describe('Variant #1: `--sourceMapMode` option value is `separate`', () => {
             describe('Variant #1: `--sourceMapMode` option value is `separate`', () => {
                 describe('Variant #1: default behaviour', () => {
                 describe('Variant #1: default behaviour', () => {
-                    const expectedSourceMapSourceName: string = path.basename(fixtureFileName);
-
                     let isFileExist: boolean,
                     let isFileExist: boolean,
-                        sourceMapObject: any;
+                        resolvedSources: string,
+                        sourceCodeContent: string,
+                        sourceMapObject: ISourceMap;
 
 
-                    before(() => {
+                    before((done) => {
                         JavaScriptObfuscatorCLI.obfuscate([
                         JavaScriptObfuscatorCLI.obfuscate([
                             'node',
                             'node',
                             'javascript-obfuscator',
                             'javascript-obfuscator',
@@ -551,10 +555,18 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         ]);
                         ]);
 
 
                         try {
                         try {
-                            const content: string = fs.readFileSync(outputSourceMapPath, { encoding: 'utf8' });
+                            sourceCodeContent = fs.readFileSync(fixtureFilePath, { encoding: 'utf8' });
+                            const sourceMapContent: string = fs.readFileSync(outputSourceMapPath, { encoding: 'utf8' });
 
 
                             isFileExist = true;
                             isFileExist = true;
-                            sourceMapObject = JSON.parse(content);
+                            sourceMapObject = JSON.parse(sourceMapContent);
+
+                            resolveSources(sourceMapObject, fixtureFilePath, fs.readFile, (error, result) => {
+                                resolvedSources = typeof result.sourcesContent[0] === 'string'
+                                    ? result.sourcesContent[0]
+                                    : '';
+                                done();
+                            });
                         } catch (e) {
                         } catch (e) {
                             isFileExist = false;
                             isFileExist = false;
                         }
                         }
@@ -568,18 +580,14 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         assert.property(sourceMapObject, 'version');
                         assert.property(sourceMapObject, 'version');
                     });
                     });
 
 
-                    it('source map from created file should contains property `sources`', () => {
-                        assert.property(sourceMapObject, 'sources');
-                    });
-
-                    it('source map source should has correct name', () => {
-                        assert.equal(sourceMapObject.sources[0], expectedSourceMapSourceName);
-                    });
-
                     it('source map from created file should contains property `names`', () => {
                     it('source map from created file should contains property `names`', () => {
                         assert.property(sourceMapObject, 'names');
                         assert.property(sourceMapObject, 'names');
                     });
                     });
 
 
+                    it('should resolve correct sources from source map', () => {
+                        assert.equal(resolvedSources, sourceCodeContent);
+                    });
+
                     after(() => {
                     after(() => {
                         rimraf.sync(outputFilePath);
                         rimraf.sync(outputFilePath);
                         rimraf.sync(outputSourceMapPath);
                         rimraf.sync(outputSourceMapPath);
@@ -587,12 +595,12 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                 });
                 });
 
 
                 describe('Variant #2: `sourceMapBaseUrl` option is set', () => {
                 describe('Variant #2: `sourceMapBaseUrl` option is set', () => {
-                    const expectedSourceMapSourceName: string = path.basename(fixtureFileName);
-
                     let isFileExist: boolean,
                     let isFileExist: boolean,
-                        sourceMapObject: any;
+                        resolvedSources: string,
+                        sourceCodeContent: string,
+                        sourceMapObject: ISourceMap;
 
 
-                    before(() => {
+                    before((done) => {
                         JavaScriptObfuscatorCLI.obfuscate([
                         JavaScriptObfuscatorCLI.obfuscate([
                             'node',
                             'node',
                             'javascript-obfuscator',
                             'javascript-obfuscator',
@@ -610,10 +618,18 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         ]);
                         ]);
 
 
                         try {
                         try {
-                            const content: string = fs.readFileSync(outputSourceMapPath, { encoding: 'utf8' });
+                            sourceCodeContent = fs.readFileSync(fixtureFilePath, { encoding: 'utf8' });
+                            const sourceMapContent: string = fs.readFileSync(outputSourceMapPath, { encoding: 'utf8' });
 
 
                             isFileExist = true;
                             isFileExist = true;
-                            sourceMapObject = JSON.parse(content);
+                            sourceMapObject = JSON.parse(sourceMapContent);
+
+                            resolveSources(sourceMapObject, fixtureFilePath, fs.readFile, (error, result) => {
+                                resolvedSources = typeof result.sourcesContent[0] === 'string'
+                                    ? result.sourcesContent[0]
+                                    : '';
+                                done();
+                            });
                         } catch (e) {
                         } catch (e) {
                             isFileExist = false;
                             isFileExist = false;
                         }
                         }
@@ -627,18 +643,14 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         assert.property(sourceMapObject, 'version');
                         assert.property(sourceMapObject, 'version');
                     });
                     });
 
 
-                    it('source map from created file should contains property `sources`', () => {
-                        assert.property(sourceMapObject, 'sources');
-                    });
-
-                    it('source map source should has correct name', () => {
-                        assert.equal(sourceMapObject.sources[0], expectedSourceMapSourceName);
-                    });
-
                     it('source map from created file should contains property `names`', () => {
                     it('source map from created file should contains property `names`', () => {
                         assert.property(sourceMapObject, 'names');
                         assert.property(sourceMapObject, 'names');
                     });
                     });
 
 
+                    it('should resolve correct sources from source map', () => {
+                        assert.equal(resolvedSources, sourceCodeContent);
+                    });
+
                     after(() => {
                     after(() => {
                         fs.unlinkSync(outputFilePath);
                         fs.unlinkSync(outputFilePath);
                         fs.unlinkSync(outputSourceMapPath);
                         fs.unlinkSync(outputSourceMapPath);
@@ -646,15 +658,16 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                 });
                 });
 
 
                 describe('Variant #3: `--sourceMapFileName` option is set', () => {
                 describe('Variant #3: `--sourceMapFileName` option is set', () => {
-                    const expectedSourceMapSourceName: string = path.basename(fixtureFileName);
                     const sourceMapFileName: string = 'test';
                     const sourceMapFileName: string = 'test';
                     const sourceMapFilePath: string = `${sourceMapFileName}.js.map`;
                     const sourceMapFilePath: string = `${sourceMapFileName}.js.map`;
                     const outputSourceMapFilePath: string = path.join(outputDirName, sourceMapFilePath);
                     const outputSourceMapFilePath: string = path.join(outputDirName, sourceMapFilePath);
 
 
                     let isFileExist: boolean,
                     let isFileExist: boolean,
-                        sourceMapObject: any;
+                        resolvedSources: string,
+                        sourceCodeContent: string,
+                        sourceMapObject: ISourceMap;
 
 
-                    before(() => {
+                    before((done) => {
                         JavaScriptObfuscatorCLI.obfuscate([
                         JavaScriptObfuscatorCLI.obfuscate([
                             'node',
                             'node',
                             'javascript-obfuscator',
                             'javascript-obfuscator',
@@ -672,10 +685,18 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         ]);
                         ]);
 
 
                         try {
                         try {
-                            const content: string = fs.readFileSync(outputSourceMapFilePath, { encoding: 'utf8' });
+                            sourceCodeContent = fs.readFileSync(fixtureFilePath, { encoding: 'utf8' });
+                            const sourceMapContent: string = fs.readFileSync(outputSourceMapFilePath, { encoding: 'utf8' });
 
 
                             isFileExist = true;
                             isFileExist = true;
-                            sourceMapObject = JSON.parse(content);
+                            sourceMapObject = JSON.parse(sourceMapContent);
+
+                            resolveSources(sourceMapObject, fixtureFilePath, fs.readFile, (error, result) => {
+                                resolvedSources = typeof result.sourcesContent[0] === 'string'
+                                    ? result.sourcesContent[0]
+                                    : '';
+                                done();
+                            });
                         } catch (e) {
                         } catch (e) {
                             isFileExist = false;
                             isFileExist = false;
                         }
                         }
@@ -689,54 +710,353 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         assert.property(sourceMapObject, 'version');
                         assert.property(sourceMapObject, 'version');
                     });
                     });
 
 
-                    it('source map from created file should contains property `sources`', () => {
-                        assert.property(sourceMapObject, 'sources');
-                    });
-
-                    it('source map source should has correct name', () => {
-                        assert.equal(sourceMapObject.sources[0], expectedSourceMapSourceName);
-                    });
-
                     it('source map from created file should contains property `names`', () => {
                     it('source map from created file should contains property `names`', () => {
                         assert.property(sourceMapObject, 'names');
                         assert.property(sourceMapObject, 'names');
                     });
                     });
 
 
+                    it('should resolve correct sources from source map', () => {
+                        assert.equal(resolvedSources, sourceCodeContent);
+                    });
+
                     after(() => {
                     after(() => {
                         fs.unlinkSync(outputFilePath);
                         fs.unlinkSync(outputFilePath);
                         fs.unlinkSync(outputSourceMapFilePath);
                         fs.unlinkSync(outputSourceMapFilePath);
                     });
                     });
                 });
                 });
+
+                describe('Variant #4: `sourceMapSourcesMode` option is set', () => {
+                    describe('Variant #1: `sourcesContent` value', () => {
+                        const expectedSourceMapSourceName: string = 'sourceMap';
+
+                        let isFileExist: boolean,
+                            resolvedSources: string,
+                            sourceCodeContent: string,
+                            sourceMapObject: ISourceMap;
+
+                        before((done) => {
+                            JavaScriptObfuscatorCLI.obfuscate([
+                                'node',
+                                'javascript-obfuscator',
+                                fixtureFilePath,
+                                '--output',
+                                outputFilePath,
+                                '--compact',
+                                'true',
+                                '--self-defending',
+                                '0',
+                                '--source-map',
+                                'true',
+                                '--source-map-sources-mode',
+                                'sources-content'
+                            ]);
+
+                            try {
+                                sourceCodeContent = fs.readFileSync(fixtureFilePath, { encoding: 'utf8' });
+                                const sourceMapContent: string = fs.readFileSync(outputSourceMapPath, { encoding: 'utf8' });
+
+                                isFileExist = true;
+                                sourceMapObject = JSON.parse(sourceMapContent);
+
+                                resolveSources(sourceMapObject, fixtureFilePath, fs.readFile, (error, result) => {
+                                    resolvedSources = typeof result.sourcesContent[0] === 'string'
+                                        ? result.sourcesContent[0]
+                                        : '';
+                                    done();
+                                });
+                            } catch (e) {
+                                isFileExist = false;
+                            }
+                        });
+
+                        it('should create file with source map in the same directory as output file', () => {
+                            assert.equal(isFileExist, true);
+                        });
+
+                        it('source map from created file should contains property `sources`', () => {
+                            assert.property(sourceMapObject, 'sources');
+                        });
+
+                        it('source map from created file should contains property `sourcesContent`', () => {
+                            assert.property(sourceMapObject, 'sourcesContent');
+                        });
+
+                        it('source map source should has correct sources', () => {
+                            assert.equal(sourceMapObject.sources[0], expectedSourceMapSourceName);
+                        });
+
+                        it('source map source should has correct sources content', () => {
+                            assert.equal(sourceMapObject.sourcesContent[0], sourceCodeContent);
+                        });
+
+                        it('should resolve correct sources from source map', () => {
+                            assert.equal(resolvedSources, sourceCodeContent);
+                        });
+
+                        after(() => {
+                            rimraf.sync(outputFilePath);
+                            rimraf.sync(outputSourceMapPath);
+                        });
+                    });
+
+                    describe('Variant #2: `sources` value', () => {
+                        const expectedSourceMapSourceName: string = path.basename(fixtureFileName);
+
+                        let isFileExist: boolean,
+                            resolvedSources: string,
+                            sourceCodeContent: string,
+                            sourceMapObject: ISourceMap;
+
+                        before((done) => {
+                            JavaScriptObfuscatorCLI.obfuscate([
+                                'node',
+                                'javascript-obfuscator',
+                                fixtureFilePath,
+                                '--output',
+                                outputFilePath,
+                                '--compact',
+                                'true',
+                                '--self-defending',
+                                '0',
+                                '--source-map',
+                                'true',
+                                '--source-map-sources-mode',
+                                'sources'
+                            ]);
+
+                            try {
+                                sourceCodeContent = fs.readFileSync(fixtureFilePath, { encoding: 'utf8' });
+                                const sourceMapContent: string = fs.readFileSync(outputSourceMapPath, { encoding: 'utf8' });
+
+                                isFileExist = true;
+                                sourceMapObject = JSON.parse(sourceMapContent);
+
+                                resolveSources(sourceMapObject, fixtureFilePath, fs.readFile, (error, result) => {
+                                    resolvedSources = typeof result.sourcesContent[0] === 'string'
+                                        ? result.sourcesContent[0]
+                                        : '';
+                                    done();
+                                });
+                            } catch (e) {
+                                isFileExist = false;
+                            }
+                        });
+
+                        it('should create file with source map in the same directory as output file', () => {
+                            assert.equal(isFileExist, true);
+                        });
+
+                        it('source map from created file should contains property `sources`', () => {
+                            assert.property(sourceMapObject, 'sources');
+                        });
+
+                        it('source map from created file should not contains property `sourcesContent`', () => {
+                            assert.notProperty(sourceMapObject, 'sourcesContent');
+                        });
+
+                        it('source map source should has correct sources', () => {
+                            assert.equal(sourceMapObject.sources[0], expectedSourceMapSourceName);
+                        });
+
+                        it('should resolve correct sources from source map', () => {
+                            assert.equal(resolvedSources, sourceCodeContent);
+                        });
+
+                        after(() => {
+                            rimraf.sync(outputFilePath);
+                            rimraf.sync(outputSourceMapPath);
+                        });
+                    });
+                });
             });
             });
 
 
             describe('Variant #2: `--sourceMapMode` option is `inline`', () => {
             describe('Variant #2: `--sourceMapMode` option is `inline`', () => {
-                let isFileExist: boolean;
+                describe('Variant #1: default behaviour', () => {
+                    let isFileExist: boolean,
+                        resolvedSources: string,
+                        sourceCodeContent: string,
+                        sourceMapObject: ISourceMap;
 
 
-                before(() => {
-                    JavaScriptObfuscatorCLI.obfuscate([
-                        'node',
-                        'javascript-obfuscator',
-                        fixtureFilePath,
-                        '--output',
-                        outputFilePath,
-                        '--compact',
-                        'true',
-                        '--self-defending',
-                        '0',
-                        '--source-map',
-                        'true',
-                        '--source-map-mode',
-                        'inline'
-                    ]);
+                    before((done) => {
+                        JavaScriptObfuscatorCLI.obfuscate([
+                            'node',
+                            'javascript-obfuscator',
+                            fixtureFilePath,
+                            '--output',
+                            outputFilePath,
+                            '--compact',
+                            'true',
+                            '--self-defending',
+                            '0',
+                            '--source-map',
+                            'true',
+                            '--source-map-mode',
+                            'inline'
+                        ]);
 
 
-                    isFileExist = fs.existsSync(outputSourceMapPath);
-                });
+                        isFileExist = fs.existsSync(outputSourceMapPath);
+
+                        sourceCodeContent = fs.readFileSync(fixtureFilePath, { encoding: 'utf8' });
+                        const obfuscatedCodeContent = fs.readFileSync(outputFilePath, { encoding: 'utf8' });
+
+                        sourceMapObject = parseSourceMapFromObfuscatedCode(obfuscatedCodeContent);
+
+                        resolveSources(sourceMapObject, fixtureFilePath, fs.readFile, (error, result) => {
+                            resolvedSources = typeof result.sourcesContent[0] === 'string'
+                                ? result.sourcesContent[0]
+                                : '';
+                            done();
+                        });
+                    });
+
+                    it('shouldn\'t create file with source map', () => {
+                        assert.equal(isFileExist, false);
+                    });
+
+                    it('should resolve correct sources from source map', () => {
+                        assert.equal(resolvedSources, sourceCodeContent);
+                    });
 
 
-                it('shouldn\'t create file with source map', () => {
-                    assert.equal(isFileExist, false);
+                    after(() => {
+                        fs.unlinkSync(outputFilePath);
+                    });
                 });
                 });
 
 
-                after(() => {
-                    fs.unlinkSync(outputFilePath);
+                describe('Variant #2: `sourceMapSourcesMode` option is set', () => {
+                    describe('Variant #1: `sourcesContent` value', () => {
+                        const expectedSourceMapSourceName: string = 'sourceMap';
+
+                        let isFileExist: boolean,
+                            resolvedSources: string,
+                            sourceCodeContent: string,
+                            sourceMapObject: ISourceMap;
+
+                        before((done) => {
+                            JavaScriptObfuscatorCLI.obfuscate([
+                                'node',
+                                'javascript-obfuscator',
+                                fixtureFilePath,
+                                '--output',
+                                outputFilePath,
+                                '--compact',
+                                'true',
+                                '--self-defending',
+                                '0',
+                                '--source-map',
+                                'true',
+                                '--source-map-mode',
+                                'inline',
+                                '--source-map-sources-mode',
+                                'sources-content'
+                            ]);
+
+                            isFileExist = fs.existsSync(outputSourceMapPath);
+
+                            sourceCodeContent = fs.readFileSync(fixtureFilePath, { encoding: 'utf8' });
+                            const obfuscatedCodeContent = fs.readFileSync(outputFilePath, { encoding: 'utf8' });
+                            sourceMapObject = parseSourceMapFromObfuscatedCode(obfuscatedCodeContent);
+
+                            resolveSources(sourceMapObject, fixtureFilePath, fs.readFile, (error, result) => {
+                                resolvedSources = typeof result.sourcesContent[0] === 'string'
+                                    ? result.sourcesContent[0]
+                                    : '';
+                                done();
+                            });
+                        });
+
+                        it('shouldn\'t create file with source map', () => {
+                            assert.equal(isFileExist, false);
+                        });
+
+                        it('source map from created file should contains property `sources`', () => {
+                            assert.property(sourceMapObject, 'sources');
+                        });
+
+                        it('source map from created file should contains property `sourcesContent`', () => {
+                            assert.property(sourceMapObject, 'sourcesContent');
+                        });
+
+                        it('source map source should has correct sources', () => {
+                            assert.equal(sourceMapObject.sources[0], expectedSourceMapSourceName);
+                        });
+
+                        it('source map source should has correct sources content', () => {
+                            assert.equal(sourceMapObject.sourcesContent[0], sourceCodeContent);
+                        });
+
+                        it('should resolve correct sources from source map', () => {
+                            assert.equal(resolvedSources, sourceCodeContent);
+                        });
+
+                        after(() => {
+                            fs.unlinkSync(outputFilePath);
+                        });
+                    });
+
+                    describe('Variant #2: `sources` value', () => {
+                        const expectedSourceMapSourceName: string = path.basename(fixtureFileName);
+
+                        let isFileExist: boolean,
+                            resolvedSources: string,
+                            sourceCodeContent: string,
+                            sourceMapObject: ISourceMap;
+
+                        before((done) => {
+                            JavaScriptObfuscatorCLI.obfuscate([
+                                'node',
+                                'javascript-obfuscator',
+                                fixtureFilePath,
+                                '--output',
+                                outputFilePath,
+                                '--compact',
+                                'true',
+                                '--self-defending',
+                                '0',
+                                '--source-map',
+                                'true',
+                                '--source-map-mode',
+                                'inline',
+                                '--source-map-sources-mode',
+                                'sources'
+                            ]);
+
+                            isFileExist = fs.existsSync(outputSourceMapPath);
+
+                            sourceCodeContent = fs.readFileSync(fixtureFilePath, { encoding: 'utf8' });
+                            const obfuscatedCodeContent = fs.readFileSync(outputFilePath, { encoding: 'utf8' });
+                            sourceMapObject = parseSourceMapFromObfuscatedCode(obfuscatedCodeContent);
+
+                            resolveSources(sourceMapObject, fixtureFilePath, fs.readFile, (error, result) => {
+                                resolvedSources = typeof result.sourcesContent[0] === 'string'
+                                    ? result.sourcesContent[0]
+                                    : '';
+                                done();
+                            });
+                        });
+
+                        it('shouldn\'t create file with source map', () => {
+                            assert.equal(isFileExist, false);
+                        });
+
+                        it('source map from created file should contains property `sources`', () => {
+                            assert.property(sourceMapObject, 'sources');
+                        });
+
+                        it('source map from created file should contains property `sourcesContent`', () => {
+                            assert.notProperty(sourceMapObject, 'sourcesContent');
+                        });
+
+                        it('source map source should has correct sources', () => {
+                            assert.equal(sourceMapObject.sources[0], expectedSourceMapSourceName);
+                        });
+
+                        it('should resolve correct sources from source map', () => {
+                            assert.equal(resolvedSources, sourceCodeContent);
+                        });
+
+                        after(() => {
+                            fs.unlinkSync(outputFilePath);
+                        });
+                    });
                 });
                 });
             });
             });
         });
         });

+ 181 - 14
test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts

@@ -1,4 +1,6 @@
+import * as fs from 'fs';
 import { assert } from 'chai';
 import { assert } from 'chai';
+import { resolveSources } from 'source-map-resolve';
 
 
 import { TDictionary } from '../../../src/types/TDictionary';
 import { TDictionary } from '../../../src/types/TDictionary';
 import { TIdentifierNamesCache } from '../../../src/types/TIdentifierNamesCache';
 import { TIdentifierNamesCache } from '../../../src/types/TIdentifierNamesCache';
@@ -7,8 +9,10 @@ import { TOptionsPreset } from '../../../src/types/options/TOptionsPreset';
 import { TTypeFromEnum } from '../../../src/types/utils/TTypeFromEnum';
 import { TTypeFromEnum } from '../../../src/types/utils/TTypeFromEnum';
 
 
 import { IObfuscationResult } from '../../../src/interfaces/source-code/IObfuscationResult';
 import { IObfuscationResult } from '../../../src/interfaces/source-code/IObfuscationResult';
+import { ISourceMap } from '../../../src/interfaces/source-code/ISourceMap';
 
 
 import { SourceMapMode } from '../../../src/enums/source-map/SourceMapMode';
 import { SourceMapMode } from '../../../src/enums/source-map/SourceMapMode';
+import { SourceMapSourcesMode } from '../../../src/enums/source-map/SourceMapSourcesMode';
 import { StringArrayEncoding } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayEncoding } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayIndexesType } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayIndexesType } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayWrappersType } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 import { StringArrayWrappersType } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
@@ -107,11 +111,13 @@ describe('JavaScriptObfuscator', () => {
 
 
         describe('`sourceMap` option is `true`', () => {
         describe('`sourceMap` option is `true`', () => {
             describe('`sourceMapMode` is `separate`', () => {
             describe('`sourceMapMode` is `separate`', () => {
-                let obfuscatedCode: string,
-                    sourceMap: string;
+                let code: string,
+                    obfuscatedCode: string,
+                    sourceMap: ISourceMap,
+                    resolvedSources: string;
 
 
-                beforeEach(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
+                beforeEach((done) => {
+                    code = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
                     const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                         code,
                         code,
                         {
                         {
@@ -121,26 +127,46 @@ describe('JavaScriptObfuscator', () => {
                     );
                     );
 
 
                     obfuscatedCode = obfuscationResult.getObfuscatedCode();
                     obfuscatedCode = obfuscationResult.getObfuscatedCode();
-                    sourceMap = JSON.parse(obfuscationResult.getSourceMap()).mappings;
+                    sourceMap = JSON.parse(obfuscationResult.getSourceMap());
+                    resolveSources(sourceMap, '/', fs.readFile, (error, result) => {
+                        resolvedSources = typeof result.sourcesContent[0] === 'string'
+                            ? result.sourcesContent[0]
+                            : '';
+                        done();
+                    });
                 });
                 });
 
 
                 it('should return correct obfuscated code', () => {
                 it('should return correct obfuscated code', () => {
                     assert.isOk(obfuscatedCode);
                     assert.isOk(obfuscatedCode);
                 });
                 });
 
 
-                it('should return correct source map', () => {
-                    assert.isOk(sourceMap);
+                it('should return correct source map mappings', () => {
+                    assert.isOk(sourceMap.mappings);
+                });
+
+                it('should define placeholder `sources` field for source map', () => {
+                    assert.deepEqual(sourceMap.sources, ['sourceMap']);
+                });
+
+                it('should define `sourcesContent` field for source map', () => {
+                    assert.isOk(sourceMap.sourcesContent);
+                });
+
+                it('should resolve correct sources from source map', () => {
+                    assert.equal(resolvedSources, code);
                 });
                 });
             });
             });
 
 
             describe('`sourceMapMode` is `inline`', () => {
             describe('`sourceMapMode` is `inline`', () => {
                 const regExp: RegExp = /sourceMappingURL=data:application\/json;base64/;
                 const regExp: RegExp = /sourceMappingURL=data:application\/json;base64/;
 
 
-                let obfuscatedCode: string,
-                    sourceMap: string;
+                let code: string,
+                    obfuscatedCode: string,
+                    sourceMap: ISourceMap,
+                    resolvedSources: string;
 
 
-                beforeEach(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
+                beforeEach((done) => {
+                    code = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
                     const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                     const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
                         code,
                         code,
                         {
                         {
@@ -151,7 +177,13 @@ describe('JavaScriptObfuscator', () => {
                     );
                     );
 
 
                     obfuscatedCode = obfuscationResult.getObfuscatedCode();
                     obfuscatedCode = obfuscationResult.getObfuscatedCode();
-                    sourceMap = JSON.parse(obfuscationResult.getSourceMap()).mappings;
+                    sourceMap = JSON.parse(obfuscationResult.getSourceMap());
+                    resolveSources(sourceMap, '/', fs.readFile, (error, result) => {
+                        resolvedSources = typeof result.sourcesContent[0] === 'string'
+                            ? result.sourcesContent[0]
+                            : '';
+                        done();
+                    });
                 });
                 });
 
 
                 it('should return correct obfuscated code', () => {
                 it('should return correct obfuscated code', () => {
@@ -162,8 +194,21 @@ describe('JavaScriptObfuscator', () => {
                     assert.match(obfuscatedCode, regExp);
                     assert.match(obfuscatedCode, regExp);
                 });
                 });
 
 
-                it('should return correct source map', () => {
-                    assert.isOk(sourceMap);
+                it('should return correct source map mappings', () => {
+                    assert.isOk(sourceMap.mappings);
+                });
+
+                it('should define placeholder `sources` field for source map', () => {
+                    assert.deepEqual(sourceMap.sources, ['sourceMap']);
+                });
+
+                it('should define `sourcesContent` field for source map', () => {
+                    assert.isOk(sourceMap.sourcesContent);
+                });
+
+                it('should resolve correct sources from source map', () => {
+                    console.log(resolvedSources);
+                    assert.equal(resolvedSources, code);
                 });
                 });
             });
             });
 
 
@@ -207,6 +252,128 @@ describe('JavaScriptObfuscator', () => {
                     assert.isNotOk(sourceMapMappings);
                     assert.isNotOk(sourceMapMappings);
                 });
                 });
             });
             });
+
+            describe('`sourceMapSourceMode` is set', () => {
+                describe('`sourcesContent` value', () => {
+                    let code: string,
+                        obfuscatedCode: string,
+                        sourceMap: ISourceMap,
+                        resolvedSources: string;
+
+                    beforeEach((done) => {
+                        code = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
+                        const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                sourceMap: true,
+                                sourceMapSourcesMode: SourceMapSourcesMode.SourcesContent
+                            }
+                        );
+
+                        obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                        sourceMap = JSON.parse(obfuscationResult.getSourceMap());
+                        resolveSources(sourceMap, '/', fs.readFile, (error, result) => {
+                            resolvedSources = typeof result.sourcesContent[0] === 'string'
+                                ? result.sourcesContent[0]
+                                : '';
+                            done();
+                        });
+                    });
+
+                    it('should return correct obfuscated code', () => {
+                        assert.isOk(obfuscatedCode);
+                    });
+
+                    it('should return correct source map mappings', () => {
+                        assert.isOk(sourceMap.mappings);
+                    });
+
+                    it('should define `sources` field for source map', () => {
+                        assert.property(sourceMap, 'sources');
+                    });
+
+                    it('should define `sourcesContent` field for source map', () => {
+                        assert.property(sourceMap, 'sourcesContent');
+                    });
+
+                    it('should define placeholder `sources` field for source map', () => {
+                        assert.deepEqual(sourceMap.sources, ['sourceMap']);
+                    });
+
+                    it('should define `sourcesContent` value with source code', () => {
+                        assert.deepEqual(sourceMap.sourcesContent[0], code);
+                    });
+
+                    it('should resolve correct sources from source map', () => {
+                        assert.equal(resolvedSources, code);
+                    });
+                });
+
+                describe('`sources` value', () => {
+                    describe('`inputFileName` option is set', () => {
+                        let code: string,
+                            obfuscatedCode: string,
+                            sourceMap: ISourceMap;
+
+                        beforeEach(() => {
+                            code = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
+                            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                                code,
+                                {
+                                    ...NO_ADDITIONAL_NODES_PRESET,
+                                    inputFileName: 'someFile.js',
+                                    sourceMap: true,
+                                    sourceMapSourcesMode: SourceMapSourcesMode.Sources
+                                }
+                            );
+
+                            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                            sourceMap = JSON.parse(obfuscationResult.getSourceMap());
+                        });
+
+                        it('should return correct obfuscated code', () => {
+                            assert.isOk(obfuscatedCode);
+                        });
+
+                        it('should return correct source map mappings', () => {
+                            assert.isOk(sourceMap.mappings);
+                        });
+
+                        it('should define `sources` field for source map', () => {
+                            assert.property(sourceMap, 'sources');
+                        });
+
+                        it('should not define `sourcesContent` field for source map', () => {
+                            assert.notProperty(sourceMap, 'sourcesContent');
+                        });
+
+                        it('should define placeholder `sources` field for source map', () => {
+                            assert.deepEqual(sourceMap.sources, ['someFile.js']);
+                        });
+                    })
+
+                    describe('`inputFileName` option is not set', () => {
+                        let testFunc: () => IObfuscationResult;
+
+                        beforeEach(() => {
+                            const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
+                            testFunc = () => JavaScriptObfuscator.obfuscate(
+                                code,
+                                {
+                                    ...NO_ADDITIONAL_NODES_PRESET,
+                                    sourceMap: true,
+                                    sourceMapSourcesMode: SourceMapSourcesMode.Sources
+                                }
+                            );
+                        });
+
+                        it('should throw an error', () => {
+                            assert.throws(testFunc, Error);
+                        });
+                    });
+                });
+            });
         });
         });
 
 
         describe('variable inside global scope', () => {
         describe('variable inside global scope', () => {

+ 234 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts

@@ -2310,5 +2310,239 @@ describe('ObjectExpressionKeysTransformer', () => {
                 assert.match(obfuscatedCode,  regExp);
                 assert.match(obfuscatedCode,  regExp);
             });
             });
         });
         });
+
+        describe('Variant #18: call expression as property value', () => {
+            describe('Variant #1: call expression as a direct property value', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{` +
+                        `'foo': *'bar',` +
+                        `'baz': *${variableMatch}\\(\\)` +
+                    `}` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/call-expression-1.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore object expression if it contains a call expression as a direct property value', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #2: call expression as an indirect property value', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{` +
+                        `'foo': *'bar',` +
+                        `'baz': *'call' *\\+ *${variableMatch}\\(\\)` +
+                    `}` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/call-expression-2.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore object expression if it contains a call expression as an indirect property value', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #3: call expression as a nested object expression as property value', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{` +
+                        `'foo': *'bar',` +
+                        `'baz': *{` +
+                            `'bark': *${variableMatch}\\(\\)` +
+                        `}` +
+                    `}` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/call-expression-3.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore object expression if it contains a call expression as a nested object expression as property value', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #4: call expression as a a property value after object expression property', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{` +
+                        `'foo': *'bar',` +
+                        `'baz': *${variableMatch},` +
+                        `'eagle': *${variableMatch}\\(\\)` +
+                    `}` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/call-expression-4.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore object expression if it contains a call expression and the previous property value is object expression', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+        });
+
+        describe('Variant #19: new expression as property value', () => {
+            describe('Variant #1: new expression as a direct property value', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{` +
+                        `'foo': *'bar',` +
+                        `'baz': *new ${variableMatch}\\(\\)` +
+                    `}` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/new-expression-1.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore object expression if it contains a new expression as a direct property value', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #2: new expression as an indirect property value', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{` +
+                        `'foo': *'bar',` +
+                        `'baz': *'call' *\\+ *new ${variableMatch}\\(\\)` +
+                    `}` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/new-expression-2.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore object expression if it contains a new expression as an indirect property value', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #3: new expression as a nested object expression as property value', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{` +
+                        `'foo': *'bar',` +
+                        `'baz': *{` +
+                            `'bark': *new ${variableMatch}\\(\\)` +
+                        `}` +
+                    `}` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/new-expression-3.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore object expression if it contains a new expression as a nested object expression as property value', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #4: new expression as a a property value after object expression property', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{` +
+                        `'foo': *'bar',` +
+                        `'baz': *${variableMatch},` +
+                        `'eagle': *new ${variableMatch}\\(\\)` +
+                    `}` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/new-expression-4.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore object expression if it contains a new expression and the previous property value is object expression', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+        });
     });
     });
 });
 });

+ 7 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/call-expression-1.js

@@ -0,0 +1,7 @@
+(function(){
+    var func = () => {};
+    var object = {
+        foo: 'bar',
+        baz: func()
+    };
+})();

+ 7 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/call-expression-2.js

@@ -0,0 +1,7 @@
+(function(){
+    var func = () => {};
+    var object = {
+        foo: 'bar',
+        baz: `call${func()}`
+    };
+})();

+ 9 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/call-expression-3.js

@@ -0,0 +1,9 @@
+(function(){
+    var func = () => {};
+    var object = {
+        foo: 'bar',
+        baz: {
+            bark: func()
+        }
+    };
+})();

+ 10 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/call-expression-4.js

@@ -0,0 +1,10 @@
+(function(){
+    var func = () => {};
+    var object = {
+        foo: 'bar',
+        baz: {
+            bark: 'hawk'
+        },
+        eagle: func()
+    };
+})();

+ 7 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/new-expression-1.js

@@ -0,0 +1,7 @@
+(function(){
+    var func = () => {};
+    var object = {
+        foo: 'bar',
+        baz: new func()
+    };
+})();

+ 7 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/new-expression-2.js

@@ -0,0 +1,7 @@
+(function(){
+    var func = () => {};
+    var object = {
+        foo: 'bar',
+        baz: `call${new func()}`
+    };
+})();

+ 9 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/new-expression-3.js

@@ -0,0 +1,9 @@
+(function(){
+    var func = () => {};
+    var object = {
+        foo: 'bar',
+        baz: {
+            bark: new func()
+        }
+    };
+})();

+ 10 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/new-expression-4.js

@@ -0,0 +1,10 @@
+(function(){
+    var func = () => {};
+    var object = {
+        foo: 'bar',
+        baz: {
+            bark: 'hawk'
+        },
+        eagle: new func()
+    };
+})();

+ 53 - 3
test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/RenamePropertiesTransformer.spec.ts

@@ -12,7 +12,7 @@ import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFac
 describe('RenamePropertiesTransformer', () => {
 describe('RenamePropertiesTransformer', () => {
     describe('transformNode', () => {
     describe('transformNode', () => {
         describe('Mode: `unsafe`', () => {
         describe('Mode: `unsafe`', () => {
-            describe('Hexadecimal identifier names generator', () => {
+            describe('Variant #1: Hexadecimal identifier names generator', () => {
                 describe('Variant #1: base properties rename', () => {
                 describe('Variant #1: base properties rename', () => {
                     const property1RegExp: RegExp = /'(_0x[a-f0-9]{4,6})': *0x1/;
                     const property1RegExp: RegExp = /'(_0x[a-f0-9]{4,6})': *0x1/;
                     const property2RegExp: RegExp = /'(_0x[a-f0-9]{4,6})': *0x2/;
                     const property2RegExp: RegExp = /'(_0x[a-f0-9]{4,6})': *0x2/;
@@ -54,7 +54,7 @@ describe('RenamePropertiesTransformer', () => {
                 });
                 });
             });
             });
 
 
-            describe('Mangled identifier names generator', () => {
+            describe('Variant #2: Mangled identifier names generator', () => {
                 describe('Variant #1: base properties mangle', () => {
                 describe('Variant #1: base properties mangle', () => {
                     const property1RegExp: RegExp = /'a': *0x1/;
                     const property1RegExp: RegExp = /'a': *0x1/;
                     const property2RegExp: RegExp = /'b': *0x2/;
                     const property2RegExp: RegExp = /'b': *0x2/;
@@ -335,7 +335,7 @@ describe('RenamePropertiesTransformer', () => {
                 });
                 });
             });
             });
 
 
-            describe('Ignored literal node type', () => {
+            describe('Variant #3: Ignored literal node type', () => {
                 describe('Variant #1: boolean literal node', () => {
                 describe('Variant #1: boolean literal node', () => {
                     const regExp: RegExp = /var obj *= *{}; *obj\[!!\[]] *= *0x1;/;
                     const regExp: RegExp = /var obj *= *{}; *obj\[!!\[]] *= *0x1;/;
 
 
@@ -359,6 +359,31 @@ describe('RenamePropertiesTransformer', () => {
                     });
                     });
                 });
                 });
             });
             });
+
+            describe('Variant #4: Prevent generation of the property names that are equal to the existing object property names', () => {
+                const regExp: RegExp = /var object *= *{'b': *'field', *'c': *'value'};/;
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/duplicated-generated-names-1.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            renameProperties: true,
+                            renamePropertiesMode: RenamePropertiesMode.Unsafe,
+                            identifierNamesGenerator: 'mangled',
+                            reservedNames: ['^a$']
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('Match #1: should skip literal property with invalid type', () => {
+                    assert.match(obfuscatedCode, regExp);
+                });
+            });
         });
         });
 
 
         describe('Mode: `safe`', () => {
         describe('Mode: `safe`', () => {
@@ -406,6 +431,31 @@ describe('RenamePropertiesTransformer', () => {
                     assert.match(obfuscatedCode, referencesRegExp);
                     assert.match(obfuscatedCode, referencesRegExp);
                 });
                 });
             });
             });
+
+            describe('Variant #2: Prevent generation of the property names that are equal to the existing object property names', () => {
+                const regExp: RegExp = /var object *= *{'b': *'field', *'c': *'value'};/;
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/duplicated-generated-names-1.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            renameProperties: true,
+                            renamePropertiesMode: RenamePropertiesMode.Safe,
+                            identifierNamesGenerator: 'mangled',
+                            reservedNames: ['^a$']
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('Match #1: should skip literal property with invalid type', () => {
+                    assert.match(obfuscatedCode, regExp);
+                });
+            });
         });
         });
 
 
         describe('Property identifier names from property identifier names cache', () => {
         describe('Property identifier names from property identifier names cache', () => {

+ 4 - 0
test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/fixtures/duplicated-generated-names-1.js

@@ -0,0 +1,4 @@
+var object = {
+    b: 'field',
+    bar: 'value'
+};

+ 225 - 76
test/functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/StringArrayScopeCallsWrapperTransformer.spec.ts

@@ -9,6 +9,7 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/N
 
 
 import { readFileAsString } from '../../../../helpers/readFileAsString';
 import { readFileAsString } from '../../../../helpers/readFileAsString';
 import { checkCodeEvaluation } from '../../../../helpers/checkCodeEvaluation';
 import { checkCodeEvaluation } from '../../../../helpers/checkCodeEvaluation';
+import { getRegExpMatch } from '../../../../helpers/getRegExpMatch';
 
 
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
 
 
@@ -644,20 +645,29 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
 
 
             describe('Variant #1: base', () => {
             describe('Variant #1: base', () => {
                 describe('Variant #1: `hexadecimal-number` indexes type', () => {
                 describe('Variant #1: `hexadecimal-number` indexes type', () => {
-                    const stringArrayCallRegExp: RegExp = new RegExp(
-                        'const f *= *function *\\(c, *d\\) *{' +
+                    const getStringArrayCallsWrapperMatch = (stringArrayCallsWrapperName: string) =>
+                        `function *${stringArrayCallsWrapperName} *\\(c, *d\\) *{` +
                             `return b\\([cd] *-(?: -)?${hexadecimalIndexMatch}, *[cd]\\);` +
                             `return b\\([cd] *-(?: -)?${hexadecimalIndexMatch}, *[cd]\\);` +
-                        '};.*' +
+                        '}';
+
+                    const stringArrayScopeCallsWrapperRegExp1: RegExp = new RegExp(
+                        'const a *= *\\[.*?];.*?' +
+                        getStringArrayCallsWrapperMatch('f')
+                    );
+                    const stringArrayScopeCallsWrapperRegExp2: RegExp = new RegExp(
+                        'const a *= *\\[.*?];.*?' +
+                        'function test *\\( *\\) *{.*' +
+                            `${getStringArrayCallsWrapperMatch('g')}.*?` +
+                        '}'
+                    );
+                    const stringArrayCallsWrapperCallsRegExp: RegExp = new RegExp(
                         `const foo *= *f\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);.*` +
                         `const foo *= *f\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);.*` +
                         `const bar *= *f\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);.*` +
                         `const bar *= *f\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);.*` +
                         `const baz *= *f\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);.*` +
                         `const baz *= *f\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);.*` +
-                        'function test *\\( *\\) *{' +
-                            'const g *= *function *\\(c, *d\\) *{' +
-                                `return b\\([cd] *-(?: -)?${hexadecimalIndexMatch}, *[cd]\\);` +
-                            '};' +
-                            `const c *= *g\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);` +
-                            `const d *= *g\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);` +
-                            `const e *= *g\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);` +
+                        'function test *\\( *\\) *{.*' +
+                            `const c *= *g\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);.*` +
+                            `const d *= *g\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);.*` +
+                            `const e *= *g\\(-? *${hexadecimalIndexMatch}\\, *-? *${hexadecimalIndexMatch}\\);.*` +
                         '}'
                         '}'
                     );
                     );
 
 
@@ -689,8 +699,16 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                         ).areSuccessEvaluations;
                         ).areSuccessEvaluations;
                     });
                     });
 
 
-                    it('should add correct scope calls wrappers', () => {
-                        assert.match(obfuscatedCode, stringArrayCallRegExp);
+                    it('Match #1: should add correct scope calls wrapper 1', () => {
+                        assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp1);
+                    });
+
+                    it('Match #2: should add correct scope calls wrapper 2', () => {
+                        assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp2);
+                    });
+
+                    it('Match #3: should add correct scope calls wrappers calls', () => {
+                        assert.match(obfuscatedCode, stringArrayCallsWrapperCallsRegExp);
                     });
                     });
 
 
                     it('should evaluate code without errors', () => {
                     it('should evaluate code without errors', () => {
@@ -699,20 +717,29 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                 });
                 });
 
 
                 describe('Variant #2: `hexadecimal-numeric-string` indexes type', () => {
                 describe('Variant #2: `hexadecimal-numeric-string` indexes type', () => {
-                    const stringArrayCallRegExp: RegExp = new RegExp(
-                        'const f *= *function *\\(c, *d\\) *{' +
+                    const getStringArrayCallsWrapperMatch = (stringArrayCallsWrapperName: string) =>
+                        `function *${stringArrayCallsWrapperName} *\\(c, *d\\) *{` +
                             `return b\\([cd] *-(?: -)?'${hexadecimalIndexMatch}', *[cd]\\);` +
                             `return b\\([cd] *-(?: -)?'${hexadecimalIndexMatch}', *[cd]\\);` +
-                        '};.*' +
+                        '}';
+
+                    const stringArrayScopeCallsWrapperRegExp1: RegExp = new RegExp(
+                        'const a *= *\\[.*?];.*?' +
+                        getStringArrayCallsWrapperMatch('f')
+                    );
+                    const stringArrayScopeCallsWrapperRegExp2: RegExp = new RegExp(
+                        'const a *= *\\[.*?];.*?' +
+                        'function test *\\( *\\) *{.*' +
+                            `${getStringArrayCallsWrapperMatch('g')}.*?` +
+                        '}'
+                    );
+                    const stringArrayCallsWrapperCallRegExp: RegExp = new RegExp(
                         `const foo *= *f\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);.*` +
                         `const foo *= *f\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);.*` +
                         `const bar *= *f\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);.*` +
                         `const bar *= *f\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);.*` +
                         `const baz *= *f\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);.*` +
                         `const baz *= *f\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);.*` +
-                        'function test *\\( *\\) *{' +
-                            'const g *= *function *\\(c, *d\\) *{' +
-                                `return b\\([cd] *-(?: -)?'${hexadecimalIndexMatch}', *[cd]\\);` +
-                            '};' +
-                            `const c *= *g\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);` +
-                            `const d *= *g\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);` +
-                            `const e *= *g\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);` +
+                        'function test *\\( *\\) *{.*' +
+                            `const c *= *g\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);.*` +
+                            `const d *= *g\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);.*` +
+                            `const e *= *g\\(-? *'${hexadecimalIndexMatch}', *-? *'${hexadecimalIndexMatch}'\\);.*` +
                         '}'
                         '}'
                     );
                     );
 
 
@@ -744,8 +771,16 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                         ).areSuccessEvaluations;
                         ).areSuccessEvaluations;
                     });
                     });
 
 
-                    it('should add correct scope calls wrappers', () => {
-                        assert.match(obfuscatedCode, stringArrayCallRegExp);
+                    it('Match #1: should add correct scope calls wrapper 1', () => {
+                        assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp1);
+                    });
+
+                    it('Match #2: should add correct scope calls wrapper 2', () => {
+                        assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp2);
+                    });
+
+                    it('Match #3: should add correct scope calls wrappers', () => {
+                        assert.match(obfuscatedCode, stringArrayCallsWrapperCallRegExp);
                     });
                     });
 
 
                     it('should evaluate code without errors', () => {
                     it('should evaluate code without errors', () => {
@@ -755,24 +790,32 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
             });
             });
 
 
             describe('Variant #2: correct chained calls', () => {
             describe('Variant #2: correct chained calls', () => {
-                const stringArrayCallRegExp: RegExp = new RegExp(
-                    'const f *= *function *\\(c, *d\\) *{' +
+                const stringArrayScopeCallsWrapperRegExp1: RegExp = new RegExp(
+                    'const a *= *\\[.*?];.*?' +
+                    'function *f *\\(c, *d\\) *{' +
                         `return b\\([cd] *-(?: -)?${hexadecimalIndexMatch}, *[cd]\\);` +
                         `return b\\([cd] *-(?: -)?${hexadecimalIndexMatch}, *[cd]\\);` +
-                    '};.*' +
-                    `const foo *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
-                    `const bar *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
-                    `const baz *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
-                    'function test *\\( *\\) *{' +
-                        'const g *= *function *\\(c, *d\\) *{' +
+                    '}.*'
+                );
+                const stringArrayScopeCallsWrapperRegExp2: RegExp = new RegExp(
+                    'const a *= *\\[.*?];.*?' +
+                    'function test *\\( *\\) *{.*' +
+                        'function *g *\\(c, *d\\) *{' +
                             `return f\\(` +
                             `return f\\(` +
                                 // order of arguments depends on the parent wrapper parameters order
                                 // order of arguments depends on the parent wrapper parameters order
                                 `[cd](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
                                 `[cd](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
                                 `[cd](?: *-(?: -)?${hexadecimalIndexMatch})?` +
                                 `[cd](?: *-(?: -)?${hexadecimalIndexMatch})?` +
                             `\\);` +
                             `\\);` +
-                        '};' +
-                        `const c *= *g\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);` +
-                        `const d *= *g\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);` +
-                        `const e *= *g\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);` +
+                        '}.*' +
+                    '}'
+                );
+                const stringArrayCallsWrapperCallRegExp: RegExp = new RegExp(
+                    `const foo *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
+                    `const bar *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
+                    `const baz *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
+                    'function test *\\( *\\) *{.*' +
+                        `const c *= *g\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
+                        `const d *= *g\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
+                        `const e *= *g\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
                     '}'
                     '}'
                 );
                 );
 
 
@@ -801,8 +844,16 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     ).areSuccessEvaluations;
                     ).areSuccessEvaluations;
                 });
                 });
 
 
-                it('should add correct scope calls wrappers', () => {
-                    assert.match(obfuscatedCode, stringArrayCallRegExp);
+                it('Match #1: should add correct scope calls wrapper 1', () => {
+                    assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp1);
+                });
+
+                it('Match #2: should add correct scope calls wrapper 2', () => {
+                    assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp2);
+                });
+
+                it('Match #3: should add correct scope calls wrappers', () => {
+                    assert.match(obfuscatedCode, stringArrayCallsWrapperCallRegExp);
                 });
                 });
 
 
                 it('should evaluate code without errors', () => {
                 it('should evaluate code without errors', () => {
@@ -811,15 +862,20 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
             });
             });
 
 
             describe('Variant #3: no wrappers on a root scope', () => {
             describe('Variant #3: no wrappers on a root scope', () => {
-                const stringArrayCallRegExp: RegExp = new RegExp(
-                    '(?<!const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};.*)' +
-                    'function test *\\( *\\) *{' +
-                        'const f *= *function *\\(c, *d\\) *{' +
+                const stringArrayScopeCallsWrapperRegExp: RegExp = new RegExp(
+                    'const a *= *\\[.*?];.*' +
+                    'function test *\\( *\\) *{.*' +
+                        'function *f*\\(c, *d\\) *{' +
                             `return b\\([cd] *-(?: -)?${hexadecimalIndexMatch}, *[cd]\\);` +
                             `return b\\([cd] *-(?: -)?${hexadecimalIndexMatch}, *[cd]\\);` +
-                        '};' +
-                        `const c *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);` +
-                        `const d *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);` +
-                        `const e *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);` +
+                        '}.*' +
+                    '}'
+                );
+                const stringArrayCallsWrapperCallRegExp: RegExp = new RegExp(
+                    '(?<!const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};.*)' +
+                    'function test *\\( *\\) *{.*' +
+                        `const c *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
+                        `const d *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
+                        `const e *= *f\\(-? *${hexadecimalIndexMatch}, *-? *${hexadecimalIndexMatch}\\);.*` +
                     '}'
                     '}'
                 );
                 );
 
 
@@ -848,8 +904,12 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     ).areSuccessEvaluations;
                     ).areSuccessEvaluations;
                 });
                 });
 
 
-                it('should add correct scope calls wrappers', () => {
-                    assert.match(obfuscatedCode, stringArrayCallRegExp);
+                it('Match #1: should add correct scope calls wrapper', () => {
+                    assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp);
+                });
+
+                it('Match #2: should add correct scope calls wrappers', () => {
+                    assert.match(obfuscatedCode, stringArrayCallsWrapperCallRegExp);
                 });
                 });
 
 
                 it('should evaluate code without errors', () => {
                 it('should evaluate code without errors', () => {
@@ -1026,15 +1086,17 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     const stringArrayWrapperArgumentsRegExpString: string = Array(5)
                     const stringArrayWrapperArgumentsRegExpString: string = Array(5)
                         .fill(`-? *${hexadecimalIndexMatch}`)
                         .fill(`-? *${hexadecimalIndexMatch}`)
                         .join(', *');
                         .join(', *');
-                    const stringArrayCallRegExp: RegExp = new RegExp(
-                        'const f *= *function *\\(c, *d, *e, *h, *i\\) *{' +
+
+                    const stringArrayScopeCallsWrapperRegExp1: RegExp = new RegExp(
+                        'const a *= *\\[.*?];.*?' +
+                        'function *f *\\(c, *d, *e, *h, *i\\) *{' +
                             `return b\\([cdehi] *-(?: -)?${hexadecimalIndexMatch}, *[cdehi]\\);` +
                             `return b\\([cdehi] *-(?: -)?${hexadecimalIndexMatch}, *[cdehi]\\);` +
-                        '};.*' +
-                        `const foo *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
-                        `const bar *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
-                        `const baz *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
-                        'function test *\\( *\\) *{' +
-                            'const g *= *function *\\(c, *d, *e, *h, *i\\) *{' +
+                        '}.*'
+                    );
+                    const stringArrayScopeCallsWrapperRegExp2: RegExp = new RegExp(
+                        'const a *= *\\[.*?];.*?' +
+                        'function test *\\( *\\) *{.*' +
+                            'function *g *\\(c, *d, *e, *h, *i\\) *{' +
                                 `return f\\(` +
                                 `return f\\(` +
                                     // order of arguments depends on the parent wrapper parameters order
                                     // order of arguments depends on the parent wrapper parameters order
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
@@ -1043,10 +1105,18 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?` +
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?` +
                                 `\\);` +
                                 `\\);` +
-                            '};' +
-                            `const c *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);` +
-                            `const d *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);` +
-                            `const e *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);` +
+                            '}.*' +
+                        '}'
+                    );
+
+                    const stringArrayCallsWrapperCallRegExp: RegExp = new RegExp(
+                        `const foo *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
+                        `const bar *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
+                        `const baz *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
+                        'function test *\\( *\\) *{.*' +
+                            `const c *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
+                            `const d *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
+                            `const e *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
                         '}'
                         '}'
                     );
                     );
 
 
@@ -1076,8 +1146,16 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                         ).areSuccessEvaluations;
                         ).areSuccessEvaluations;
                     });
                     });
 
 
-                    it('should add correct scope calls wrappers', () => {
-                        assert.match(obfuscatedCode, stringArrayCallRegExp);
+                    it('Match #1: should add correct scope calls wrapper 1', () => {
+                        assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp1);
+                    });
+
+                    it('Match #2: should add correct scope calls wrapper 2', () => {
+                        assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp2);
+                    });
+
+                    it('Match #3: should add correct scope calls wrappers', () => {
+                        assert.match(obfuscatedCode, stringArrayCallsWrapperCallRegExp);
                     });
                     });
 
 
                     it('should evaluate code without errors', () => {
                     it('should evaluate code without errors', () => {
@@ -1089,15 +1167,17 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     const stringArrayWrapperArgumentsRegExpString: string = Array(5)
                     const stringArrayWrapperArgumentsRegExpString: string = Array(5)
                         .fill(`(?:-? *${hexadecimalIndexMatch}|(?:'.{4}'))`)
                         .fill(`(?:-? *${hexadecimalIndexMatch}|(?:'.{4}'))`)
                         .join(', *');
                         .join(', *');
-                    const stringArrayCallRegExp: RegExp = new RegExp(
-                        'const f *= *function *\\(c, *d, *e, *h, *i\\) *{' +
+
+                    const stringArrayScopeCallsWrapperRegExp1: RegExp = new RegExp(
+                        'const a *= *\\[.*?];.*?' +
+                        'function *f *\\(c, *d, *e, *h, *i\\) *{' +
                             `return b\\([cdehi] *-(?: -)?${hexadecimalIndexMatch}, *[cdehi]\\);` +
                             `return b\\([cdehi] *-(?: -)?${hexadecimalIndexMatch}, *[cdehi]\\);` +
-                        '};.*' +
-                        `const foo *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
-                        `const bar *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
-                        `const baz *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
-                        'function test *\\( *\\) *{' +
-                            'const g *= *function *\\(c, *d, *e, *h, *i\\) *{' +
+                        '}.*'
+                    );
+                    const stringArrayScopeCallsWrapperRegExp2: RegExp = new RegExp(
+                        'const a *= *\\[.*?];.*?' +
+                        'function test *\\( *\\) *{.*' +
+                            'function *g *\\(c, *d, *e, *h, *i\\) *{' +
                                 `return f\\(` +
                                 `return f\\(` +
                                     // order of arguments depends on the parent wrapper parameters order
                                     // order of arguments depends on the parent wrapper parameters order
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
@@ -1106,10 +1186,18 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?` +
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?` +
                                 `\\);` +
                                 `\\);` +
-                            '};' +
-                            `const c *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);` +
-                            `const d *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);` +
-                            `const e *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);` +
+                            '}.*' +
+                        '}'
+                    );
+
+                    const stringArrayCallsWrapperCallRegExp: RegExp = new RegExp(
+                        `const foo *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
+                        `const bar *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
+                        `const baz *= *f\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
+                        'function test *\\( *\\) *{.*' +
+                            `const c *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
+                            `const d *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
+                            `const e *= *g\\(${stringArrayWrapperArgumentsRegExpString}\\);.*` +
                         '}'
                         '}'
                     );
                     );
 
 
@@ -1142,8 +1230,16 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                         ).areSuccessEvaluations;
                         ).areSuccessEvaluations;
                     });
                     });
 
 
-                    it('should add correct scope calls wrappers', () => {
-                        assert.match(obfuscatedCode, stringArrayCallRegExp);
+                    it('Match #1: should add correct scope calls wrapper 1', () => {
+                        assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp1);
+                    });
+
+                    it('Match #2: should add correct scope calls wrapper 2', () => {
+                        assert.match(obfuscatedCode, stringArrayScopeCallsWrapperRegExp2);
+                    });
+
+                    it('Match #3: should add correct scope calls wrappers', () => {
+                        assert.match(obfuscatedCode, stringArrayCallsWrapperCallRegExp);
                     });
                     });
 
 
                     it('should evaluate code without errors', () => {
                     it('should evaluate code without errors', () => {
@@ -1151,6 +1247,59 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     });
                     });
                 });
                 });
             });
             });
+
+            describe('Variant #6: different indexes for calls wrappers', () => {
+                const getStringArrayScopeCallsWrapperMatch = (stringArrayScopeCallsWrapperName: string) =>
+                    `function *${stringArrayScopeCallsWrapperName} *\\(e, *f\\) *{` +
+                        `return b\\([ef] *-(?: -)?(${hexadecimalIndexMatch}), *[ef]\\);` +
+                    '}.*';
+
+                const stringArrayScopeCallsWrapperIndexRegExp1: RegExp = new RegExp(
+                    getStringArrayScopeCallsWrapperMatch('c')
+                );
+                const stringArrayScopeCallsWrapperIndexRegExp2: RegExp = new RegExp(
+                    getStringArrayScopeCallsWrapperMatch('d')
+                );
+
+                const differentIndexesMatchesSamplesCount: number = 20;
+                let differentIndexesMatchesCount: number = 0;
+                let differentIndexesMatchesDelta: number = 3;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/function-calls-wrappers-different-indexes.js');
+
+                    for (let i = 0; i < differentIndexesMatchesSamplesCount; i++) {
+                        const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                stringArray: true,
+                                stringArrayThreshold: 1,
+                                stringArrayWrappersChainedCalls: true,
+                                stringArrayWrappersCount: 2,
+                                stringArrayWrappersParametersMaxCount: 2,
+                                stringArrayWrappersType: StringArrayWrappersType.Function
+                            }
+                        ).getObfuscatedCode();
+
+                        const indexMatch1 = getRegExpMatch(obfuscatedCode, stringArrayScopeCallsWrapperIndexRegExp1);
+                        const indexMatch2 = getRegExpMatch(obfuscatedCode, stringArrayScopeCallsWrapperIndexRegExp2);
+
+                        if (indexMatch1 !== indexMatch2) {
+                            differentIndexesMatchesCount++;
+                        }
+                    }
+                });
+
+                it('Should generate a different indexes for different string array scope calls wrappers', () => {
+                    assert.closeTo(
+                        differentIndexesMatchesCount,
+                        differentIndexesMatchesSamplesCount,
+                        differentIndexesMatchesDelta
+                    );
+                });
+            });
         });
         });
     });
     });
 
 

+ 2 - 0
test/functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/fixtures/function-calls-wrappers-different-indexes.js

@@ -0,0 +1,2 @@
+var foo = 'foo'
+var bar = 'bar'

+ 18 - 0
test/functional-tests/options/OptionsNormalizer.spec.ts

@@ -301,6 +301,24 @@ describe('OptionsNormalizer', () => {
                     assert.deepEqual(optionsPreset, expectedOptionsPreset);
                     assert.deepEqual(optionsPreset, expectedOptionsPreset);
                 });
                 });
             });
             });
+
+            describe('Variant #5: relative path', () => {
+                before(() => {
+                    optionsPreset = getNormalizedOptions({
+                        ...getDefaultOptions(),
+                        inputFileName: 'baz/bar/foo.js'
+                    });
+
+                    expectedOptionsPreset = {
+                        ...getDefaultOptions(),
+                        inputFileName: 'baz/bar/foo.js'
+                    };
+                });
+
+                it('should normalize options preset', () => {
+                    assert.deepEqual(optionsPreset, expectedOptionsPreset);
+                });
+            });
         });
         });
 
 
         describe('identifierNamesCacheRule', () => {
         describe('identifierNamesCacheRule', () => {

+ 72 - 0
test/functional-tests/options/input-file-name/Validation.spec.ts

@@ -0,0 +1,72 @@
+import { assert } from 'chai';
+
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
+import { SourceMapSourcesMode } from '../../../../src/enums/source-map/SourceMapSourcesMode';
+
+describe('`inputFileName` validation', () => {
+    describe('IsInputFileName', () => {
+        describe('Variant #1: positive validation', () => {
+            describe('Variant #1: empty string when `sourceMapSourcesMode: \'sources-content\'', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            inputFileName: '',
+                            sourceMapSourcesMode: SourceMapSourcesMode.SourcesContent
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+
+            describe('Variant #2: string with input file name when `sourceMapSourcesMode: \'sources\'', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            inputFileName: 'some-file.js',
+                            sourceMapSourcesMode: SourceMapSourcesMode.Sources
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+        });
+
+        describe('Variant #2: negative validation', () => {
+            describe('Variant #1: empty string when `sourceMapSourcesMode: \'sources\'', () => {
+                const expectedError: string = 'should not be empty';
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            inputFileName: '',
+                            sourceMapSourcesMode: SourceMapSourcesMode.Sources
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should not pass validation', () => {
+                    assert.throws(testFunc, expectedError);
+                });
+            });
+        });
+    });
+});

+ 7 - 0
test/helpers/atob.ts

@@ -0,0 +1,7 @@
+/**
+ * @param {string} encodedString
+ * @returns {string}
+ */
+export function atob (encodedString: string): string {
+    return Buffer.from(encodedString, 'base64').toString();
+}

+ 11 - 0
test/helpers/parseSourceMapFromObfuscatedCode.ts

@@ -0,0 +1,11 @@
+import { ISourceMap } from '../../src/interfaces/source-code/ISourceMap';
+
+import { atob } from './atob';
+
+/**
+ * @param {string} obfuscatedCodeWithInlineSourceMap
+ * @returns {ISourceMap}
+ */
+export function parseSourceMapFromObfuscatedCode (obfuscatedCodeWithInlineSourceMap: string): ISourceMap {
+    return JSON.parse(atob(obfuscatedCodeWithInlineSourceMap.split('base64,')[1]));
+}

+ 1 - 0
test/index.spec.ts

@@ -136,6 +136,7 @@ import './functional-tests/options/OptionsNormalizer.spec';
 import './functional-tests/options/domain-lock-destination/Validation.spec';
 import './functional-tests/options/domain-lock-destination/Validation.spec';
 import './functional-tests/options/domain-lock/Validation.spec';
 import './functional-tests/options/domain-lock/Validation.spec';
 import './functional-tests/options/identifier-names-cache/Validation.spec';
 import './functional-tests/options/identifier-names-cache/Validation.spec';
+import './functional-tests/options/input-file-name/Validation.spec';
 import './functional-tests/storages/string-array-transformers/string-array-storage/StringArrayStorage.spec';
 import './functional-tests/storages/string-array-transformers/string-array-storage/StringArrayStorage.spec';
 
 
 /**
 /**

+ 19 - 10
test/unit-tests/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.spec.ts

@@ -56,6 +56,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
 
             before(() => {
             before(() => {
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArray: true,
                     stringArrayThreshold: 1
                     stringArrayThreshold: 1
                 });
                 });
 
 
@@ -74,11 +75,11 @@ describe('StringArrayStorageAnalyzer', () => {
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
             });
             });
 
 
-            it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+            it('Variant #2: should return correct string array storage item data for literal node #2', () => {
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
             });
             });
 
 
-            it('Variant #3: should return correct string array storage item data for literal node #1', () => {
+            it('Variant #3: should return correct string array storage item data for literal node #3', () => {
                 assert.deepEqual(stringArrayStorageItemData3, expectedStringArrayStorageItemData3);
                 assert.deepEqual(stringArrayStorageItemData3, expectedStringArrayStorageItemData3);
             });
             });
         });
         });
@@ -101,6 +102,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
 
             before(() => {
             before(() => {
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArray: true,
                     stringArrayThreshold: 1
                     stringArrayThreshold: 1
                 });
                 });
 
 
@@ -118,7 +120,7 @@ describe('StringArrayStorageAnalyzer', () => {
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
             });
             });
 
 
-            it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+            it('Variant #2: should return correct string array storage item data for literal node #2', () => {
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
             });
             });
         });
         });
@@ -141,6 +143,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
 
             before(() => {
             before(() => {
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArray: true,
                     stringArrayThreshold: 1
                     stringArrayThreshold: 1
                 });
                 });
 
 
@@ -158,7 +161,7 @@ describe('StringArrayStorageAnalyzer', () => {
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
             });
             });
 
 
-            it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+            it('Variant #2: should return correct string array storage item data for literal node #2', () => {
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
             });
             });
         });
         });
@@ -181,6 +184,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
 
             before(() => {
             before(() => {
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArray: true,
                     stringArrayThreshold: 1
                     stringArrayThreshold: 1
                 });
                 });
 
 
@@ -208,7 +212,7 @@ describe('StringArrayStorageAnalyzer', () => {
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
             });
             });
 
 
-            it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+            it('Variant #2: should return correct string array storage item data for literal node #2', () => {
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
             });
             });
         });
         });
@@ -232,6 +236,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
 
             before(() => {
             before(() => {
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArray: true,
                     stringArrayThreshold: 1
                     stringArrayThreshold: 1
                 });
                 });
 
 
@@ -249,7 +254,7 @@ describe('StringArrayStorageAnalyzer', () => {
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
             });
             });
 
 
-            it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+            it('Variant #2: should return correct string array storage item data for literal node #2', () => {
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
             });
             });
         });
         });
@@ -274,6 +279,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
 
                 before(() => {
                 before(() => {
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                        stringArray: true,
                         stringArrayThreshold: 0
                         stringArrayThreshold: 0
                     });
                     });
 
 
@@ -291,7 +297,7 @@ describe('StringArrayStorageAnalyzer', () => {
                     assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                     assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                 });
                 });
 
 
-                it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+                it('Variant #2: should return correct string array storage item data for literal node #2', () => {
                     assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                     assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                 });
                 });
             });
             });
@@ -315,6 +321,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
 
                 before(() => {
                 before(() => {
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                        stringArray: true,
                         stringArrayThreshold: 1
                         stringArrayThreshold: 1
                     });
                     });
 
 
@@ -332,7 +339,7 @@ describe('StringArrayStorageAnalyzer', () => {
                     assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                     assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                 });
                 });
 
 
-                it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+                it('Variant #2: should return correct string array storage item data for literal node #2', () => {
                     assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                     assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                 });
                 });
             });
             });
@@ -375,7 +382,7 @@ describe('StringArrayStorageAnalyzer', () => {
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                 assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
             });
             });
 
 
-            it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+            it('Variant #2: should return correct string array storage item data for literal node #2', () => {
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                 assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
             });
             });
         });
         });
@@ -393,6 +400,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
 
                 before(() => {
                 before(() => {
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                        stringArray: true,
                         stringArrayThreshold: 0
                         stringArrayThreshold: 0
                     });
                     });
 
 
@@ -410,7 +418,7 @@ describe('StringArrayStorageAnalyzer', () => {
                     assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                     assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
                 });
                 });
 
 
-                it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+                it('Variant #2: should return correct string array storage item data for literal node #2', () => {
                     assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                     assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
                 });
                 });
             });
             });
@@ -433,6 +441,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
 
                 before(() => {
                 before(() => {
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                        stringArray: true,
                         stringArrayThreshold: 0.5,
                         stringArrayThreshold: 0.5,
                         seed: 3
                         seed: 3
                     });
                     });

+ 304 - 190
test/unit-tests/cli/utils/ObfuscatedCodeFileUtils.spec.ts

@@ -307,202 +307,316 @@ describe('obfuscatedCodeFileUtils', () => {
 
 
     describe('getOutputSourceMapPath', () => {
     describe('getOutputSourceMapPath', () => {
         describe('Variant #1: output code path is a file path', () => {
         describe('Variant #1: output code path is a file path', () => {
-            const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
-            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const outputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js.map');
-
-            let outputSourceMapPath: string;
-
-            before(() => {
-                const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
-                    rawInputPath,
-                    {
-                        output: rawOutputPath
-                    }
-                );
-                outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath);
-            });
-
-            it('should return output path for source map', () => {
-                assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
-            });
-        });
-
-        describe('Variant #2: output code path is a directory path and source map file name is not set', () => {
-            const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
-            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const outputCodePath: string = path.join(tmpDirectoryPath, 'output');
-
-            let testFunc: () => string;
-
-            before(() => {
-                const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
-                    rawInputPath,
-                    {
-                        output: rawOutputPath
-                    }
-                );
-                testFunc = () => obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath);
-            });
-
-            it('should throw an error if output code path is a directory path and source map file name is not set', () => {
-                assert.throws(testFunc, Error);
-            });
-        });
-
-        describe('Variant #3: output code path with dot', () => {
-            const rawInputPath: string = path.join(tmpDirectoryPath, 'input.with.dot', 'test-input.js');
-            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output.with.dot', 'test-output.js');
-            const outputCodePath: string = path.join(tmpDirectoryPath, 'output.with.dot', 'test-output.js');
-            const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output.with.dot', 'test-output.js.map');
-
-            let outputSourceMapPath: string;
+            describe('Variant #1: file path with directory', () => {
+                describe('Variant #1: source map file name is not set', () => {
+                    describe('Variant #1: base output code path', () => {
+                        const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
+                        const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                        const outputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                        const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js.map');
+
+                        let outputSourceMapPath: string;
+
+                        before(() => {
+                            const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                                rawInputPath,
+                                {
+                                    output: rawOutputPath
+                                }
+                            );
+                            outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath);
+                        });
+
+                        it('should return output path for source map', () => {
+                            assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                        });
+                    });
 
 
-            before(() => {
-                const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
-                    rawInputPath,
-                    {
-                        output: rawOutputPath
-                    }
-                );
-                outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath);
-            });
+                    describe('Variant #2: output code path with dot', () => {
+                        const rawInputPath: string = path.join(tmpDirectoryPath, 'input.with.dot', 'test-input.js');
+                        const rawOutputPath: string = path.join(tmpDirectoryPath, 'output.with.dot', 'test-output.js');
+                        const outputCodePath: string = path.join(tmpDirectoryPath, 'output.with.dot', 'test-output.js');
+                        const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output.with.dot', 'test-output.js.map');
+
+                        let outputSourceMapPath: string;
+
+                        before(() => {
+                            const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                                rawInputPath,
+                                {
+                                    output: rawOutputPath
+                                }
+                            );
+                            outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath);
+                        });
+
+                        it('should return output path for source map', () => {
+                            assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                        });
+                    });
+                });
 
 
-            it('should return output path for source map', () => {
-                assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
-            });
-        });
+                describe('Variant #2: source map file name is set', () => {
+                    describe('Variant #1: source map file name without extension is set', () => {
+                        const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
+                        const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                        const outputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                        const sourceMapFileName: string = 'foo';
+                        const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'foo.js.map');
+
+                        let outputSourceMapPath: string;
+
+                        before(() => {
+                            const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                                rawInputPath,
+                                {
+                                    output: rawOutputPath
+                                }
+                            );
+                            outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                        });
+
+                        it('should return output path for source map', () => {
+                            assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                        });
+                    });
 
 
-        describe('Variant #4: source map file name without extension is set', () => {
-            const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
-            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const outputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const sourceMapFileName: string = 'foo';
-            const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'foo.js.map');
+                    describe('Variant #2: source map file name with wrong extension is set', () => {
+                        const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
+                        const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                        const outputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                        const sourceMapFileName: string = 'foo.js';
+                        const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'foo.js.map');
+
+                        let outputSourceMapPath: string;
+
+                        before(() => {
+                            const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                                rawInputPath,
+                                {
+                                    output: rawOutputPath
+                                }
+                            );
+                            outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                        });
+
+                        it('should return output path for source map', () => {
+                            assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                        });
+                    });
 
 
-            let outputSourceMapPath: string;
+                    describe('Variant #3: source map file name with valid extension is set', () => {
+                        const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
+                        const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                        const outputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                        const sourceMapFileName: string = 'foo.js.map';
+                        const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'foo.js.map');
+
+                        let outputSourceMapPath: string;
+
+                        before(() => {
+                            const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                                rawInputPath,
+                                {
+                                    output: rawOutputPath
+                                }
+                            );
+                            outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                        });
+
+                        it('should return output path for source map', () => {
+                            assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                        });
+                    });
 
 
-            before(() => {
-                const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
-                    rawInputPath,
-                    {
-                        output: rawOutputPath
-                    }
-                );
-                outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
-            });
+                    describe('Variant #4: source map file name contains directories', () => {
+                        const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
+                        const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                        const outputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                        const sourceMapFileName: string = path.join('parent', 'foo.js.map');
+                        const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'parent', 'foo.js.map');
+
+                        let outputSourceMapPath: string;
+
+                        before(() => {
+                            const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                                rawInputPath,
+                                {
+                                    output: rawOutputPath
+                                }
+                            );
+                            outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                        });
+
+                        it('should return output path for source map', () => {
+                            assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                        });
+                    });
 
 
-            it('should return output path for source map', () => {
-                assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                    describe('Variant #5: output code path with dot', () => {
+                        const rawInputPath: string = path.join(tmpDirectoryPath, 'input.with.dot', 'test-input.js');
+                        const rawOutputPath: string = path.join(tmpDirectoryPath, 'output.with.dot', 'test-output.js');
+                        const outputCodePath: string = path.join(tmpDirectoryPath, 'output.with.dot', 'test-output.js');
+                        const sourceMapFileName: string = path.join('parent', 'foo.js.map');
+                        const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output.with.dot', 'parent', 'foo.js.map');
+
+                        let outputSourceMapPath: string;
+
+                        before(() => {
+                            const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                                rawInputPath,
+                                {
+                                    output: rawOutputPath
+                                }
+                            );
+                            outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                        });
+
+                        it('should return output path for source map', () => {
+                            assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                        });
+                    });
+                });
             });
             });
-        });
-
-        describe('Variant #5: output code path is a directory path and source map file name without extension is set', () => {
-            const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
-            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const outputCodePath: string = path.join(tmpDirectoryPath, 'output');
-            const sourceMapFileName: string = 'foo';
-            const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'foo.js.map');
 
 
-            let outputSourceMapPath: string;
+            describe('Variant #2: file path without directory', () => {
+                describe('Variant #1: source map file name is not set', () => {
+                    describe('Variant #1: base output code path', () => {
+                        const rawInputPath: string = 'test-input.js';
+                        const rawOutputPath: string = 'test-output.js';
+                        const outputCodePath: string = 'test-output.js';
+                        const expectedOutputSourceMapPath: string = 'test-output.js.map';
+
+                        let outputSourceMapPath: string;
+
+                        before(() => {
+                            const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                                rawInputPath,
+                                {
+                                    output: rawOutputPath
+                                }
+                            );
+                            outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath);
+                        });
+
+                        it('should return output path for source map', () => {
+                            assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                        });
+                    });
+                });
 
 
-            before(() => {
-                const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
-                    rawInputPath,
-                    {
-                        output: rawOutputPath
-                    }
-                );
-                outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                describe('Variant #2: source map file name is set', () => {
+                    describe('Variant #1: base output code path', () => {
+                        const rawInputPath: string = 'test-input.js';
+                        const rawOutputPath: string = 'test-output.js';
+                        const outputCodePath: string = 'test-output.js';
+                        const outputSourceMapFileName: string = 'test-output-source-map';
+                        const expectedOutputSourceMapPath: string = 'test-output-source-map.js.map';
+
+                        let outputSourceMapPath: string;
+
+                        before(() => {
+                            const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                                rawInputPath,
+                                {
+                                    output: rawOutputPath
+                                }
+                            );
+                            outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(
+                                outputCodePath,
+                                outputSourceMapFileName
+                            );
+                        });
+
+                        it('should return output path for source map', () => {
+                            assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                        });
+                    });
+                });
             });
             });
 
 
-            it('should return output path for source map', () => {
-                assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
-            });
-        });
+            describe('Variant #10: Win32 environment', () => {
+                describe('Variant #1: source map file name is a file name without extension', () => {
+                    const rawInputPath: string = path.join('C:\\', tmpDirectoryPath, 'input', 'test-input.js');
+                    const rawOutputPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
+                    const outputCodePath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
+                    const sourceMapFileName: string = path.join('foo');
+                    const expectedOutputSourceMapPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'foo.js.map');
 
 
-        describe('Variant #6: source map file name with wrong extension is set', () => {
-            const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
-            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const outputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const sourceMapFileName: string = 'foo.js';
-            const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'foo.js.map');
+                    let outputSourceMapPath: string;
 
 
-            let outputSourceMapPath: string;
+                    before(() => {
+                        const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                            rawInputPath,
+                            {
+                                output: rawOutputPath
+                            }
+                        );
+                        outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                    });
 
 
-            before(() => {
-                const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
-                    rawInputPath,
-                    {
-                        output: rawOutputPath
-                    }
-                );
-                outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
-            });
+                    it('should return output path for source map', () => {
+                        assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                    });
+                });
 
 
-            it('should return output path for source map', () => {
-                assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
-            });
-        });
+                describe('Variant #2: source map file name is a file name with an extension', () => {
+                    const rawInputPath: string = path.join('C:\\', tmpDirectoryPath, 'input', 'test-input.js');
+                    const rawOutputPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
+                    const outputCodePath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
+                    const sourceMapFileName: string = path.join('foo.js.map');
+                    const expectedOutputSourceMapPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'foo.js.map');
 
 
-        describe('Variant #7: source map file name with valid extension is set', () => {
-            const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
-            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const outputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const sourceMapFileName: string = 'foo.js.map';
-            const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'foo.js.map');
-
-            let outputSourceMapPath: string;
+                    let outputSourceMapPath: string;
 
 
-            before(() => {
-                const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
-                    rawInputPath,
-                    {
-                        output: rawOutputPath
-                    }
-                );
-                outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
-            });
+                    before(() => {
+                        const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                            rawInputPath,
+                            {
+                                output: rawOutputPath
+                            }
+                        );
+                        outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                    });
 
 
-            it('should return output path for source map', () => {
-                assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
-            });
-        });
+                    it('should return output path for source map', () => {
+                        assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                    });
+                });
 
 
-        describe('Variant #8: source map file name is a path', () => {
-            const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
-            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const outputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
-            const sourceMapFileName: string = path.join('parent', 'foo.js.map');
-            const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'parent', 'foo.js.map');
+                describe('Variant #3: output path and win32 path in source map file name', () => {
+                    const rawInputPath: string = path.join('C:\\', tmpDirectoryPath, 'input', 'test-input.js');
+                    const rawOutputPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
+                    const outputCodePath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
+                    const sourceMapFileName: string = path.join('C:\\', 'parent', 'foo.js.map');
+                    const expectedOutputSourceMapPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'parent', 'foo.js.map');
 
 
-            let outputSourceMapPath: string;
+                    let outputSourceMapPath: string;
 
 
-            before(() => {
-                const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
-                    rawInputPath,
-                    {
-                        output: rawOutputPath
-                    }
-                );
-                outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
-            });
+                    before(() => {
+                        const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
+                            rawInputPath,
+                            {
+                                output: rawOutputPath
+                            }
+                        );
+                        outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                    });
 
 
-            it('should return output path for source map', () => {
-                assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                    it('should return output path for source map', () => {
+                        assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                    });
+                });
             });
             });
         });
         });
 
 
-        describe('Variant #9: Win32 environment', () => {
-            describe('Variant #1: output code path is a directory path', () => {
-                const rawInputPath: string = path.join('C:\\', tmpDirectoryPath, 'input', 'test-input.js');
-                const rawOutputPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
-                const outputCodePath: string = path.join('C:\\', tmpDirectoryPath, 'output');
-                const sourceMapFileName: string = path.join('foo');
-                const expectedOutputSourceMapPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'foo.js.map');
+        describe(`Variant #2: output code path is a directory path`, () => {
+            describe('Variant #1: source map file name is not set', () => {
+                const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
+                const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                const outputCodePath: string = path.join(tmpDirectoryPath, 'output');
 
 
-                let outputSourceMapPath: string;
+                let testFunc: () => string;
 
 
                 before(() => {
                 before(() => {
                     const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
                     const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
@@ -511,20 +625,20 @@ describe('obfuscatedCodeFileUtils', () => {
                             output: rawOutputPath
                             output: rawOutputPath
                         }
                         }
                     );
                     );
-                    outputSourceMapPath = obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                    testFunc = () => obfuscatedCodeFileUtils.getOutputSourceMapPath(outputCodePath);
                 });
                 });
 
 
-                it('should return output path for source map', () => {
-                    assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                it('should throw an error if output code path is a directory path and source map file name is not set', () => {
+                    assert.throws(testFunc, Error);
                 });
                 });
             });
             });
 
 
-            describe('Variant #2: source map file name is a file name without extension', () => {
-                const rawInputPath: string = path.join('C:\\', tmpDirectoryPath, 'input', 'test-input.js');
-                const rawOutputPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
-                const outputCodePath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
-                const sourceMapFileName: string = path.join('foo');
-                const expectedOutputSourceMapPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'foo.js.map');
+            describe('Variant #2: source map file name without extension is set', () => {
+                const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
+                const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                const outputCodePath: string = path.join(tmpDirectoryPath, 'output');
+                const sourceMapFileName: string = 'foo';
+                const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'foo.js.map');
 
 
                 let outputSourceMapPath: string;
                 let outputSourceMapPath: string;
 
 
@@ -543,12 +657,12 @@ describe('obfuscatedCodeFileUtils', () => {
                 });
                 });
             });
             });
 
 
-            describe('Variant #3: source map file name is a file name with an extension', () => {
-                const rawInputPath: string = path.join('C:\\', tmpDirectoryPath, 'input', 'test-input.js');
-                const rawOutputPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
-                const outputCodePath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
-                const sourceMapFileName: string = path.join('foo.js.map');
-                const expectedOutputSourceMapPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'foo.js.map');
+            describe('Variant #2: source map file name with extension is set', () => {
+                const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
+                const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+                const outputCodePath: string = path.join(tmpDirectoryPath, 'output');
+                const sourceMapFileName: string = 'foo.js.map';
+                const expectedOutputSourceMapPath: string = path.join(tmpDirectoryPath, 'output', 'foo.js.map');
 
 
                 let outputSourceMapPath: string;
                 let outputSourceMapPath: string;
 
 
@@ -567,12 +681,12 @@ describe('obfuscatedCodeFileUtils', () => {
                 });
                 });
             });
             });
 
 
-            describe('Variant #4: output path and win32 path in source map file name', () => {
+            describe('Variant #3: Win32 environment', () => {
                 const rawInputPath: string = path.join('C:\\', tmpDirectoryPath, 'input', 'test-input.js');
                 const rawInputPath: string = path.join('C:\\', tmpDirectoryPath, 'input', 'test-input.js');
                 const rawOutputPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
                 const rawOutputPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
-                const outputCodePath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'test-output.js');
-                const sourceMapFileName: string = path.join('C:\\', 'parent', 'foo.js.map');
-                const expectedOutputSourceMapPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'parent', 'foo.js.map');
+                const outputCodePath: string = path.join('C:\\', tmpDirectoryPath, 'output');
+                const sourceMapFileName: string = path.join('foo');
+                const expectedOutputSourceMapPath: string = path.join('C:\\', tmpDirectoryPath, 'output', 'foo.js.map');
 
 
                 let outputSourceMapPath: string;
                 let outputSourceMapPath: string;
 
 
@@ -592,7 +706,7 @@ describe('obfuscatedCodeFileUtils', () => {
             });
             });
         });
         });
 
 
-        describe('Variant #10: empty paths', () => {
+        describe('Variant #3: empty paths', () => {
             const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
             const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
             const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
             const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
 
 

+ 44 - 8
test/unit-tests/utils/CryptUtils.spec.ts

@@ -8,6 +8,7 @@ import { ICryptUtils } from '../../../src/interfaces/utils/ICryptUtils';
 import { IInversifyContainerFacade } from '../../../src/interfaces/container/IInversifyContainerFacade';
 import { IInversifyContainerFacade } from '../../../src/interfaces/container/IInversifyContainerFacade';
 
 
 import { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade';
 import { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade';
+import { atob } from '../../helpers/atob';
 
 
 describe('CryptUtils', () => {
 describe('CryptUtils', () => {
     let cryptUtils: ICryptUtils;
     let cryptUtils: ICryptUtils;
@@ -21,30 +22,65 @@ describe('CryptUtils', () => {
 
 
     describe('btoa', () => {
     describe('btoa', () => {
        describe('Variant #1: basic', () => {
        describe('Variant #1: basic', () => {
-           const expectedString: string = 'c3RyaW5n';
+           const expectedEncodedString: string = 'c3RyaW5n';
+           const expectedDecodedString: string = 'string';
 
 
-           let string: string;
+           let encodedString: string,
+               decodedString: string;
 
 
            before(() => {
            before(() => {
-               string = cryptUtils.btoa('string');
+               encodedString = cryptUtils.btoa('string');
+               decodedString = atob(encodedString);
            });
            });
 
 
            it('should create a base-64 encoded string from a given string', () => {
            it('should create a base-64 encoded string from a given string', () => {
-               assert.equal(string, expectedString);
+               assert.equal(encodedString, expectedEncodedString);
+           });
+
+           it('should create encoded string that can be successfully decoded', () => {
+               assert.equal(decodedString, expectedDecodedString);
            });
            });
        });
        });
 
 
         describe('Variant #2: padding characters', () => {
         describe('Variant #2: padding characters', () => {
-            const expectedString: string = 'c3RyaQ==';
+            const expectedEncodedString: string = 'c3RyaQ==';
+            const expectedDecodedString: string = 'stri';
 
 
-            let string: string;
+            let encodedString: string,
+                decodedString: string;
 
 
             before(() => {
             before(() => {
-                string = cryptUtils.btoa('stri');
+                encodedString = cryptUtils.btoa('stri');
+                decodedString = atob(encodedString);
             });
             });
 
 
             it('should create a base-64 encoded string from a given string with padding characters', () => {
             it('should create a base-64 encoded string from a given string with padding characters', () => {
-                assert.equal(string, expectedString);
+                assert.equal(encodedString, expectedEncodedString);
+            });
+
+            it('should create encoded string that can be successfully decoded', () => {
+                assert.equal(decodedString, expectedDecodedString);
+            });
+        });
+
+        describe('Variant #3: cyrillic string', () => {
+            const expectedEncodedString: string = '0YLQtdGB0YI=';
+            const expectedDecodedString: string = 'тест';
+
+            let encodedString: string,
+                decodedString: string;
+
+            before(() => {
+                encodedString = cryptUtils.btoa('тест');
+                decodedString = atob(encodedString);
+            });
+
+            it('should create a base-64 encoded string from a given string', () => {
+                assert.equal(encodedString, expectedEncodedString);
+            });
+
+            it('should create encoded string with a cyrillic characters that can be successfully decoded', () => {
+                assert.equal(decodedString, expectedDecodedString);
             });
             });
         });
         });
     });
     });

+ 282 - 231
yarn.lock

@@ -405,19 +405,19 @@
   resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz"
   resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz"
   integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==
   integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==
 
 
-"@es-joy/jsdoccomment@^0.8.0":
-  version "0.8.0"
-  resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.8.0.tgz#1add451f50f57597676ab85ee7bd0a273d7b7c43"
-  integrity sha512-Xd3GzYsL2sz2pcdtYt5Q0Wz1ol/o9Nt2UQL4nFPDcaEomvPmwjJsbjkKx1SKhl2h3TgwazNBLdcNr2m0UiGiFA==
+"@es-joy/jsdoccomment@0.9.0-alpha.1":
+  version "0.9.0-alpha.1"
+  resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.9.0-alpha.1.tgz#f48bd162e185ec7f9f222273a282d10e52fe52f7"
+  integrity sha512-Clxxc0PwpISoYYBibA+1L2qFJ7gvFVhI2Hos87S06K+Q0cXdOhZQJNKWuaQGPAeHjZEuUB/YoWOfwjuF2wirqA==
   dependencies:
   dependencies:
-    comment-parser "^1.1.5"
+    comment-parser "1.1.6-beta.0"
     esquery "^1.4.0"
     esquery "^1.4.0"
     jsdoc-type-pratt-parser "1.0.4"
     jsdoc-type-pratt-parser "1.0.4"
 
 
-"@eslint/eslintrc@^0.4.2":
-  version "0.4.2"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179"
-  integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==
+"@eslint/eslintrc@^0.4.3":
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
+  integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==
   dependencies:
   dependencies:
     ajv "^6.12.4"
     ajv "^6.12.4"
     debug "^4.1.1"
     debug "^4.1.1"
@@ -574,27 +574,35 @@
   resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1"
   resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1"
   integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==
   integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==
 
 
-"@types/[email protected]9":
-  version "4.2.19"
-  resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.19.tgz#80f286b515897413c7a35bdda069cc80f2344233"
-  integrity sha512-jRJgpRBuY+7izT7/WNXP/LsMO9YonsstuL+xuvycDyESpoDoIAsMd7suwpB4h9oEWB+ZlPTqJJ8EHomzNhwTPQ==
+"@types/[email protected].21":
+  version "4.2.21"
+  resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.21.tgz#9f35a5643129df132cf3b5c1ec64046ea1af0650"
+  integrity sha512-yd+9qKmJxm496BOV9CMNaey8TWsikaZOwMRwPHQIjcOJM9oV+fi9ZMNw3JsVnbEEbo2gRTDnGEBv8pjyn67hNg==
 
 
-"@types/[email protected].2":
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.1.2.tgz#0f397c17e9d5a9e83914e767ca6f419b2ded09dd"
-  integrity sha512-OYwnnh2D7QAleRpPWnBQBfDZMlapMHoNeuvyEg7WrDiMApgcKOnXgyiVAl+OzBvhyQmfYkx7YtFXOm8E9IYsNw==
+"@types/[email protected].3":
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.1.3.tgz#d19fe9391288d60fdccd87632bfc9ab2b4523fea"
+  integrity sha512-X6c6ghhe4/sQh4XzcZWSFaTAUOda38GQHmq9BUanYkOE/EO7ZrkazwKmtsj3xzTjkLWmwULE++23g3d3CCWaWw==
 
 
 "@types/color-name@^1.1.1":
 "@types/color-name@^1.1.1":
   version "1.1.1"
   version "1.1.1"
   resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz"
   resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz"
   integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
   integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
 
 
-"@types/[email protected]":
-  version "0.0.6"
-  resolved "https://registry.npmjs.org/@types/escodegen/-/escodegen-0.0.6.tgz"
-  integrity sha1-UjCpznluBCzabwhtvxnyLqMwZZw=
+"@types/[email protected]":
+  version "0.0.7"
+  resolved "https://registry.yarnpkg.com/@types/escodegen/-/escodegen-0.0.7.tgz#a1c3e3dfd76da89f01d7d196eebe227ebe4b6eec"
+  integrity sha512-46oENdSRNEJXCNrPJoC3vRolZJpfeEm7yvATkd2bCncKFG0PUEyfBCaoacfpcXH4Y5RRuqdVj3J7TI+wwn2SbQ==
+
+"@types/[email protected]":
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.1.tgz#8dc390a7b4f9dd9f1284629efce982e41612116e"
+  integrity sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==
+  dependencies:
+    "@types/eslint" "*"
+    "@types/estree" "*"
 
 
-"@types/[email protected]", "@types/eslint-scope@^3.7.0":
+"@types/eslint-scope@^3.7.0":
   version "3.7.0"
   version "3.7.0"
   resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz"
   resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz"
   integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==
   integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==
@@ -610,22 +618,17 @@
     "@types/estree" "*"
     "@types/estree" "*"
     "@types/json-schema" "*"
     "@types/json-schema" "*"
 
 
-"@types/[email protected].0":
-  version "5.1.0"
-  resolved "https://registry.npmjs.org/@types/estraverse/-/estraverse-5.1.0.tgz"
-  integrity sha512-vH2ItsZq47KprWHdv8OMjlfpygPHp1P7X4zuJuTghXldyezatpaotNSujld/HNsxh9TUS7+JRB0HEldkv67qaw==
+"@types/[email protected].1":
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/@types/estraverse/-/estraverse-5.1.1.tgz#1a220a4f78c10a739496a18fa067e2a2429ae881"
+  integrity sha512-AUnklMz90PdmlN4TFqnHC5hP9eRwNQdXgI8aXsSVVMMr2U6Nwo7n4QJxzOiUtVZy/0JFg5oA4PxVh8UBtUELyw==
   dependencies:
   dependencies:
     "@types/estree" "*"
     "@types/estree" "*"
 
 
-"@types/estree@*", "@types/[email protected]":
-  version "0.0.49"
-  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.49.tgz#3facb98ebcd4114a4ecef74e0de2175b56fd4464"
-  integrity sha512-K1AFuMe8a+pXmfHTtnwBvqoEylNKVeaiKYkjmcEAdytMQVJ/i9Fu7sc13GxgXdO49gkE7Hy8SyJonUZUn+eVaw==
-
-"@types/estree@^0.0.48":
-  version "0.0.48"
-  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.48.tgz#18dc8091b285df90db2f25aa7d906cfc394b7f74"
-  integrity sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==
+"@types/estree@*", "@types/[email protected]", "@types/estree@^0.0.50":
+  version "0.0.50"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
+  integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
 
 
 "@types/events@*":
 "@types/events@*":
   version "3.0.0"
   version "3.0.0"
@@ -666,15 +669,20 @@
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
   integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
   integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
 
 
+"@types/json-schema@^7.0.8":
+  version "7.0.8"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818"
+  integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==
+
 "@types/json5@^0.0.29":
 "@types/json5@^0.0.29":
   version "0.0.29"
   version "0.0.29"
   resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
   resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
   integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
   integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
 
 
-"@types/[email protected].0":
-  version "2.3.0"
-  resolved "https://registry.npmjs.org/@types/md5/-/md5-2.3.0.tgz"
-  integrity sha512-556YJ7ejzxIqSSxzyGGpctuZOarNZJt/zlEkhmmDc1f/slOEANHuwu2ZX7YaZ40rMiWoxt8GvAhoDpW1cmSy6A==
+"@types/[email protected].1":
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.1.tgz#010bcf3bb50a2cff3a574cb1c0b4051a9c67d6bc"
+  integrity sha512-OK3oe+ALIoPSo262lnhAYwpqFNXbiwH2a+0+Z5YBnkQEwWD8fk5+PIeRhYA48PzvX9I4SGNpWy+9bLj8qz92RQ==
   dependencies:
   dependencies:
     "@types/node" "*"
     "@types/node" "*"
 
 
@@ -683,17 +691,17 @@
   resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz"
   resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz"
   integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
   integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
 
 
-"@types/[email protected].1":
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz"
-  integrity sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==
+"@types/[email protected].2":
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-1.0.2.tgz#8d0bad7aa793abe551860be1f7ae7f3198c16666"
+  integrity sha512-o0K1tSO0Dx5X6xlU5F1D6625FawhC3dU3iqr25lluNv/+/QIVH8RLNEiVokgIZo+mz+87w/3Mkg/VvQS+J51fQ==
   dependencies:
   dependencies:
     "@types/node" "*"
     "@types/node" "*"
 
 
-"@types/mocha@8.2.2":
-  version "8.2.2"
-  resolved "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz"
-  integrity sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==
+"@types/mocha@9.0.0":
+  version "9.0.0"
+  resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.0.0.tgz#3205bcd15ada9bc681ac20bef64e9e6df88fd297"
+  integrity sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==
 
 
 "@types/[email protected]":
 "@types/[email protected]":
   version "4.0.0"
   version "4.0.0"
@@ -707,10 +715,10 @@
   resolved "https://registry.npmjs.org/@types/node/-/node-13.9.3.tgz"
   resolved "https://registry.npmjs.org/@types/node/-/node-13.9.3.tgz"
   integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA==
   integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA==
 
 
-"@types/node@16.0.0":
-  version "16.0.0"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-16.0.0.tgz#067a6c49dc7a5c2412a505628e26902ae967bf6f"
-  integrity sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg==
+"@types/node@16.4.1":
+  version "16.4.1"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.1.tgz#9fad171a5b701613ee8a6f4ece3c88b1034b1b03"
+  integrity sha512-UW7cbLqf/Wu5XH2RKKY1cHwUNLicIDRLMraYKz+HHAerJ0ZffUEk+fMnd8qU2JaS6cAy0r8tsaf7yqHASf/Y0Q==
 
 
 "@types/normalize-package-data@^2.4.0":
 "@types/normalize-package-data@^2.4.0":
   version "2.4.0"
   version "2.4.0"
@@ -722,10 +730,10 @@
   resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz"
   resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz"
   integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
   integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
 
 
-"@types/[email protected].0":
-  version "3.0.0"
-  resolved "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.0.tgz"
-  integrity sha512-7WhJ0MdpFgYQPXlF4Dx+DhgvlPCfz/x5mHaeDQAKhcenvQP1KCpLQ18JklAqeGMYSAT2PxLpzd0g2/HE7fj7hQ==
+"@types/[email protected].1":
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.1.tgz#1bbc106f0978742289103e080d4b41b3b4656e58"
+  integrity sha512-CAoSlbco40aKZ0CkelBF2g3JeN6aioRaTVnqSX5pWsn/WApm6IDxI4e4tD9D0dY/meCkyyleP1IQDVN13F4maA==
   dependencies:
   dependencies:
     "@types/glob" "*"
     "@types/glob" "*"
     "@types/node" "*"
     "@types/node" "*"
@@ -752,73 +760,73 @@
   resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.16.2.tgz#8db514b059c1b2ae14ce9d7bb325296de6a9a0fa"
   resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.16.2.tgz#8db514b059c1b2ae14ce9d7bb325296de6a9a0fa"
   integrity sha512-vKx7WNQNZDyJveYcHAm9ZxhqSGLYwoyLhrHjLBOkw3a7cT76sTdjgtwyijhk1MaHyRIuSztcVwrUOO/NEu68Dw==
   integrity sha512-vKx7WNQNZDyJveYcHAm9ZxhqSGLYwoyLhrHjLBOkw3a7cT76sTdjgtwyijhk1MaHyRIuSztcVwrUOO/NEu68Dw==
 
 
-"@typescript-eslint/[email protected].1":
-  version "4.28.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.1.tgz#c045e440196ae45464e08e20c38aff5c3a825947"
-  integrity sha512-9yfcNpDaNGQ6/LQOX/KhUFTR1sCKH+PBr234k6hI9XJ0VP5UqGxap0AnNwBnWFk1MNyWBylJH9ZkzBXC+5akZQ==
+"@typescript-eslint/[email protected].4":
+  version "4.28.4"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.4.tgz#e73c8cabbf3f08dee0e1bda65ed4e622ae8f8921"
+  integrity sha512-s1oY4RmYDlWMlcV0kKPBaADn46JirZzvvH7c2CtAqxCY96S538JRBAzt83RrfkDheV/+G/vWNK0zek+8TB3Gmw==
   dependencies:
   dependencies:
-    "@typescript-eslint/experimental-utils" "4.28.1"
-    "@typescript-eslint/scope-manager" "4.28.1"
+    "@typescript-eslint/experimental-utils" "4.28.4"
+    "@typescript-eslint/scope-manager" "4.28.4"
     debug "^4.3.1"
     debug "^4.3.1"
     functional-red-black-tree "^1.0.1"
     functional-red-black-tree "^1.0.1"
     regexpp "^3.1.0"
     regexpp "^3.1.0"
     semver "^7.3.5"
     semver "^7.3.5"
     tsutils "^3.21.0"
     tsutils "^3.21.0"
 
 
-"@typescript-eslint/[email protected].1":
-  version "4.28.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.1.tgz#3869489dcca3c18523c46018b8996e15948dbadc"
-  integrity sha512-n8/ggadrZ+uyrfrSEchx3jgODdmcx7MzVM2sI3cTpI/YlfSm0+9HEUaWw3aQn2urL2KYlWYMDgn45iLfjDYB+Q==
+"@typescript-eslint/[email protected].4":
+  version "4.28.4"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.4.tgz#9c70c35ebed087a5c70fb0ecd90979547b7fec96"
+  integrity sha512-OglKWOQRWTCoqMSy6pm/kpinEIgdcXYceIcH3EKWUl4S8xhFtN34GQRaAvTIZB9DD94rW7d/U7tUg3SYeDFNHA==
   dependencies:
   dependencies:
     "@types/json-schema" "^7.0.7"
     "@types/json-schema" "^7.0.7"
-    "@typescript-eslint/scope-manager" "4.28.1"
-    "@typescript-eslint/types" "4.28.1"
-    "@typescript-eslint/typescript-estree" "4.28.1"
+    "@typescript-eslint/scope-manager" "4.28.4"
+    "@typescript-eslint/types" "4.28.4"
+    "@typescript-eslint/typescript-estree" "4.28.4"
     eslint-scope "^5.1.1"
     eslint-scope "^5.1.1"
     eslint-utils "^3.0.0"
     eslint-utils "^3.0.0"
 
 
-"@typescript-eslint/[email protected].1":
-  version "4.28.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.1.tgz#5181b81658414f47291452c15bf6cd44a32f85bd"
-  integrity sha512-UjrMsgnhQIIK82hXGaD+MCN8IfORS1CbMdu7VlZbYa8LCZtbZjJA26De4IPQB7XYZbL8gJ99KWNj0l6WD0guJg==
+"@typescript-eslint/[email protected].4":
+  version "4.28.4"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.4.tgz#bc462dc2779afeefdcf49082516afdc3e7b96fab"
+  integrity sha512-4i0jq3C6n+og7/uCHiE6q5ssw87zVdpUj1k6VlVYMonE3ILdFApEzTWgppSRG4kVNB/5jxnH+gTeKLMNfUelQA==
   dependencies:
   dependencies:
-    "@typescript-eslint/scope-manager" "4.28.1"
-    "@typescript-eslint/types" "4.28.1"
-    "@typescript-eslint/typescript-estree" "4.28.1"
+    "@typescript-eslint/scope-manager" "4.28.4"
+    "@typescript-eslint/types" "4.28.4"
+    "@typescript-eslint/typescript-estree" "4.28.4"
     debug "^4.3.1"
     debug "^4.3.1"
 
 
-"@typescript-eslint/[email protected].1":
-  version "4.28.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz#fd3c20627cdc12933f6d98b386940d8d0ce8a991"
-  integrity sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA==
+"@typescript-eslint/[email protected].4":
+  version "4.28.4"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.4.tgz#bdbce9b6a644e34f767bd68bc17bb14353b9fe7f"
+  integrity sha512-ZJBNs4usViOmlyFMt9X9l+X0WAFcDH7EdSArGqpldXu7aeZxDAuAzHiMAeI+JpSefY2INHrXeqnha39FVqXb8w==
   dependencies:
   dependencies:
-    "@typescript-eslint/types" "4.28.1"
-    "@typescript-eslint/visitor-keys" "4.28.1"
+    "@typescript-eslint/types" "4.28.4"
+    "@typescript-eslint/visitor-keys" "4.28.4"
 
 
-"@typescript-eslint/[email protected].1":
-  version "4.28.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.1.tgz#d0f2ecbef3684634db357b9bbfc97b94b828f83f"
-  integrity sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg==
+"@typescript-eslint/[email protected].4":
+  version "4.28.4"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.4.tgz#41acbd79b5816b7c0dd7530a43d97d020d3aeb42"
+  integrity sha512-3eap4QWxGqkYuEmVebUGULMskR6Cuoc/Wii0oSOddleP4EGx1tjLnZQ0ZP33YRoMDCs5O3j56RBV4g14T4jvww==
 
 
-"@typescript-eslint/[email protected].1":
-  version "4.28.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz#af882ae41740d1f268e38b4d0fad21e7e8d86a81"
-  integrity sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ==
+"@typescript-eslint/[email protected].4":
+  version "4.28.4"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.4.tgz#252e6863278dc0727244be9e371eb35241c46d00"
+  integrity sha512-z7d8HK8XvCRyN2SNp+OXC2iZaF+O2BTquGhEYLKLx5k6p0r05ureUtgEfo5f6anLkhCxdHtCf6rPM1p4efHYDQ==
   dependencies:
   dependencies:
-    "@typescript-eslint/types" "4.28.1"
-    "@typescript-eslint/visitor-keys" "4.28.1"
+    "@typescript-eslint/types" "4.28.4"
+    "@typescript-eslint/visitor-keys" "4.28.4"
     debug "^4.3.1"
     debug "^4.3.1"
     globby "^11.0.3"
     globby "^11.0.3"
     is-glob "^4.0.1"
     is-glob "^4.0.1"
     semver "^7.3.5"
     semver "^7.3.5"
     tsutils "^3.21.0"
     tsutils "^3.21.0"
 
 
-"@typescript-eslint/[email protected].1":
-  version "4.28.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz#162a515ee255f18a6068edc26df793cdc1ec9157"
-  integrity sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg==
+"@typescript-eslint/[email protected].4":
+  version "4.28.4"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.4.tgz#92dacfefccd6751cbb0a964f06683bfd72d0c4d3"
+  integrity sha512-NIAXAdbz1XdOuzqkJHjNKXKj8QQ4cv5cxR/g0uQhCYf/6//XrmfpaYsM7PnBcNbfvTDLUkqQ5TPNm1sozDdTWg==
   dependencies:
   dependencies:
-    "@typescript-eslint/types" "4.28.1"
+    "@typescript-eslint/types" "4.28.4"
     eslint-visitor-keys "^2.0.0"
     eslint-visitor-keys "^2.0.0"
 
 
 "@ungap/[email protected]":
 "@ungap/[email protected]":
@@ -826,125 +834,125 @@
   resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz"
   resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz"
   integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
   integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz"
-  integrity sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
+  integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==
   dependencies:
   dependencies:
-    "@webassemblyjs/helper-numbers" "1.11.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
+    "@webassemblyjs/helper-numbers" "1.11.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz"
-  integrity sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
+  integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz"
-  integrity sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
+  integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz"
-  integrity sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
+  integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz"
-  integrity sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
+  integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==
   dependencies:
   dependencies:
-    "@webassemblyjs/floating-point-hex-parser" "1.11.0"
-    "@webassemblyjs/helper-api-error" "1.11.0"
+    "@webassemblyjs/floating-point-hex-parser" "1.11.1"
+    "@webassemblyjs/helper-api-error" "1.11.1"
     "@xtuc/long" "4.2.2"
     "@xtuc/long" "4.2.2"
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz"
-  integrity sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
+  integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz"
-  integrity sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
+  integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==
   dependencies:
   dependencies:
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/helper-buffer" "1.11.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
-    "@webassemblyjs/wasm-gen" "1.11.0"
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/helper-buffer" "1.11.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+    "@webassemblyjs/wasm-gen" "1.11.1"
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz"
-  integrity sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
+  integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==
   dependencies:
   dependencies:
     "@xtuc/ieee754" "^1.2.0"
     "@xtuc/ieee754" "^1.2.0"
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz"
-  integrity sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
+  integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==
   dependencies:
   dependencies:
     "@xtuc/long" "4.2.2"
     "@xtuc/long" "4.2.2"
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz"
-  integrity sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
+  integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz"
-  integrity sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==
-  dependencies:
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/helper-buffer" "1.11.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
-    "@webassemblyjs/helper-wasm-section" "1.11.0"
-    "@webassemblyjs/wasm-gen" "1.11.0"
-    "@webassemblyjs/wasm-opt" "1.11.0"
-    "@webassemblyjs/wasm-parser" "1.11.0"
-    "@webassemblyjs/wast-printer" "1.11.0"
-
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz"
-  integrity sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
+  integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==
+  dependencies:
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/helper-buffer" "1.11.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+    "@webassemblyjs/helper-wasm-section" "1.11.1"
+    "@webassemblyjs/wasm-gen" "1.11.1"
+    "@webassemblyjs/wasm-opt" "1.11.1"
+    "@webassemblyjs/wasm-parser" "1.11.1"
+    "@webassemblyjs/wast-printer" "1.11.1"
+
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
+  integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==
   dependencies:
   dependencies:
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
-    "@webassemblyjs/ieee754" "1.11.0"
-    "@webassemblyjs/leb128" "1.11.0"
-    "@webassemblyjs/utf8" "1.11.0"
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+    "@webassemblyjs/ieee754" "1.11.1"
+    "@webassemblyjs/leb128" "1.11.1"
+    "@webassemblyjs/utf8" "1.11.1"
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz"
-  integrity sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
+  integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==
   dependencies:
   dependencies:
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/helper-buffer" "1.11.0"
-    "@webassemblyjs/wasm-gen" "1.11.0"
-    "@webassemblyjs/wasm-parser" "1.11.0"
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/helper-buffer" "1.11.1"
+    "@webassemblyjs/wasm-gen" "1.11.1"
+    "@webassemblyjs/wasm-parser" "1.11.1"
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz"
-  integrity sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
+  integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==
   dependencies:
   dependencies:
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/helper-api-error" "1.11.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
-    "@webassemblyjs/ieee754" "1.11.0"
-    "@webassemblyjs/leb128" "1.11.0"
-    "@webassemblyjs/utf8" "1.11.0"
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/helper-api-error" "1.11.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+    "@webassemblyjs/ieee754" "1.11.1"
+    "@webassemblyjs/leb128" "1.11.1"
+    "@webassemblyjs/utf8" "1.11.1"
 
 
-"@webassemblyjs/[email protected].0":
-  version "1.11.0"
-  resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz"
-  integrity sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==
+"@webassemblyjs/[email protected].1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
+  integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==
   dependencies:
   dependencies:
-    "@webassemblyjs/ast" "1.11.0"
+    "@webassemblyjs/ast" "1.11.1"
     "@xtuc/long" "4.2.2"
     "@xtuc/long" "4.2.2"
 
 
 "@webpack-cli/configtest@^1.0.4":
 "@webpack-cli/configtest@^1.0.4":
@@ -1207,6 +1215,11 @@ at-least-node@^1.0.0:
   resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz"
   resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz"
   integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
   integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
 
 
+atob@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
 available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2:
 available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2:
   version "1.0.2"
   version "1.0.2"
   resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz"
   resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz"
@@ -1509,10 +1522,10 @@ commander@^7.0.0:
   resolved "https://registry.npmjs.org/commander/-/commander-7.0.0.tgz"
   resolved "https://registry.npmjs.org/commander/-/commander-7.0.0.tgz"
   integrity sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA==
   integrity sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA==
 
 
[email protected].5, comment-parser@^1.1.5:
-  version "1.1.5"
-  resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.1.5.tgz#453627ef8f67dbcec44e79a9bd5baa37f0bce9b2"
-  integrity sha512-RePCE4leIhBlmrqiYTvaqEeGYg7qpSl4etaIabKtdOQVi+mSTIBBklGUwIr79GXYnl3LpMwmDw4KeR2stNc6FA==
[email protected].6-beta.0:
+  version "1.1.6-beta.0"
+  resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.1.6-beta.0.tgz#57e503b18d0a5bd008632dcc54b1f95c2fffe8f6"
+  integrity sha512-q3cA8TSMyqW7wcPSYWzbO/rMahnXgzs4SLG/UIWXdEsnXTFPZkEkWAdNgPiHig2OzxgpPLOh4WwsmClDxndwHw==
 
 
 commondir@^1.0.1:
 commondir@^1.0.1:
   version "1.0.1"
   version "1.0.1"
@@ -1643,6 +1656,13 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
   dependencies:
   dependencies:
     ms "^2.1.1"
     ms "^2.1.1"
 
 
+debug@^4.3.2:
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
+  integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
+  dependencies:
+    ms "2.1.2"
+
 decamelize@^1.2.0:
 decamelize@^1.2.0:
   version "1.2.0"
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -1653,6 +1673,11 @@ decamelize@^4.0.0:
   resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz"
   resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz"
   integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
   integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
 
 
+decode-uri-component@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+  integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
 deep-eql@^3.0.1:
 deep-eql@^3.0.1:
   version "3.0.1"
   version "3.0.1"
   resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz"
   resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz"
@@ -1826,10 +1851,10 @@ es-abstract@^1.18.0-next.2:
     string.prototype.trimstart "^1.0.4"
     string.prototype.trimstart "^1.0.4"
     unbox-primitive "^1.0.1"
     unbox-primitive "^1.0.1"
 
 
-es-module-lexer@^0.6.0:
-  version "0.6.0"
-  resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.6.0.tgz#e72ab05b7412e62b9be37c37a09bdb6000d706f0"
-  integrity sha512-f8kcHX1ArhllUtb/wVSyvygoKCznIjnxhLxy7TCvIiMdT7fL4ZDTIKaadMe6eLvOXg6Wk02UeoFgUoZ2EKZZUA==
+es-module-lexer@^0.7.1:
+  version "0.7.1"
+  resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.7.1.tgz#c2c8e0f46f2df06274cdaf0dd3f3b33e0a0b267d"
+  integrity sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==
 
 
 es-to-primitive@^1.2.1:
 es-to-primitive@^1.2.1:
   version "1.2.1"
   version "1.2.1"
@@ -1902,14 +1927,14 @@ [email protected]:
     resolve "^1.20.0"
     resolve "^1.20.0"
     tsconfig-paths "^3.9.0"
     tsconfig-paths "^3.9.0"
 
 
-eslint-plugin-jsdoc@35.4.1:
-  version "35.4.1"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.4.1.tgz#7fb2a8c9bd8e304ab1feee48aa34544df5f79839"
-  integrity sha512-lnpu2Bj+ta2eAqwCWnb6f3Xjc78TWKo/oMCpDH5NfpPhYnePNtGZJzoAMgU5uo9BQqmXJ8pql8aiodOhg82ofw==
+eslint-plugin-jsdoc@35.5.1:
+  version "35.5.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.5.1.tgz#45932ee22669bbe06c97b82b936d56361efad370"
+  integrity sha512-pPYPWtsykwVEue1tYEyoppBj4dgF7XicF67tLLLraY6RQYBq7qMKjUHji19+hfiTtYKKBD0YfeK8hgjPAE5viw==
   dependencies:
   dependencies:
-    "@es-joy/jsdoccomment" "^0.8.0"
-    comment-parser "1.1.5"
-    debug "^4.3.1"
+    "@es-joy/jsdoccomment" "0.9.0-alpha.1"
+    comment-parser "1.1.6-beta.0"
+    debug "^4.3.2"
     esquery "^1.4.0"
     esquery "^1.4.0"
     jsdoc-type-pratt-parser "^1.0.4"
     jsdoc-type-pratt-parser "^1.0.4"
     lodash "^4.17.21"
     lodash "^4.17.21"
@@ -1953,9 +1978,18 @@ [email protected]:
     esrecurse "^4.1.0"
     esrecurse "^4.1.0"
     estraverse "^4.1.1"
     estraverse "^4.1.1"
 
 
[email protected], eslint-scope@^5.1.1, "eslint-scope@github:eslint/eslint-scope#master":
[email protected], eslint-scope@^5.1.1:
   version "5.1.1"
   version "5.1.1"
-  resolved "https://codeload.github.com/eslint/eslint-scope/tar.gz/0b4a5f132fb65520eee31bcd166078656b6e158e"
+  resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz"
+  integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^4.1.1"
+
[email protected]:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-6.0.0.tgz#9cf45b13c5ac8f3d4c50f46a5121f61b3e318978"
+  integrity sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==
   dependencies:
   dependencies:
     esrecurse "^4.3.0"
     esrecurse "^4.3.0"
     estraverse "^5.2.0"
     estraverse "^5.2.0"
@@ -2000,13 +2034,13 @@ eslint-visitor-keys@^2.0.0:
   resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz"
   resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz"
   integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
   integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
 
 
[email protected]0.0:
-  version "7.30.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.30.0.tgz#6d34ab51aaa56112fd97166226c9a97f505474f8"
-  integrity sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==
[email protected]1.0:
+  version "7.31.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.31.0.tgz#f972b539424bf2604907a970860732c5d99d3aca"
+  integrity sha512-vafgJpSh2ia8tnTkNUkwxGmnumgckLh5aAbLa1xRmIn9+owi8qBNGKL+B881kNKNTy7FFqTEkpNkUvmw0n6PkA==
   dependencies:
   dependencies:
     "@babel/code-frame" "7.12.11"
     "@babel/code-frame" "7.12.11"
-    "@eslint/eslintrc" "^0.4.2"
+    "@eslint/eslintrc" "^0.4.3"
     "@humanwhocodes/config-array" "^0.5.0"
     "@humanwhocodes/config-array" "^0.5.0"
     ajv "^6.10.0"
     ajv "^6.10.0"
     chalk "^4.0.0"
     chalk "^4.0.0"
@@ -3941,6 +3975,15 @@ schema-utils@^3.0.0:
     ajv "^6.12.5"
     ajv "^6.12.5"
     ajv-keywords "^3.5.2"
     ajv-keywords "^3.5.2"
 
 
+schema-utils@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
+  integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
+  dependencies:
+    "@types/json-schema" "^7.0.8"
+    ajv "^6.12.5"
+    ajv-keywords "^3.5.2"
+
 "semver@2 || 3 || 4 || 5", semver@^5.4.1:
 "semver@2 || 3 || 4 || 5", semver@^5.4.1:
   version "5.7.1"
   version "5.7.1"
   resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz"
   resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz"
@@ -4066,6 +4109,14 @@ source-list-map@^2.0.1:
   resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz"
   resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz"
   integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
   integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
 
 
+source-map-resolve@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
+  integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
+  dependencies:
+    atob "^2.1.2"
+    decode-uri-component "^0.2.0"
+
 [email protected], source-map-support@^0.5.17, source-map-support@~0.5.19:
 [email protected], source-map-support@^0.5.17, source-map-support@~0.5.19:
   version "0.5.19"
   version "0.5.19"
   resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz"
   resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz"
@@ -4402,10 +4453,10 @@ [email protected]:
     micromatch "^4.0.0"
     micromatch "^4.0.0"
     semver "^7.3.4"
     semver "^7.3.4"
 
 
-ts-node@10.0.0:
-  version "10.0.0"
-  resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.0.0.tgz#05f10b9a716b0b624129ad44f0ea05dac84ba3be"
-  integrity sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg==
+ts-node@10.1.0:
+  version "10.1.0"
+  resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.1.0.tgz#e656d8ad3b61106938a867f69c39a8ba6efc966e"
+  integrity sha512-6szn3+J9WyG2hE+5W8e0ruZrzyk1uFLYye6IGMBadnOzDh8aP7t8CbFpsfCiEx2+wMixAhjFt7lOZC4+l+WbEA==
   dependencies:
   dependencies:
     "@tsconfig/node10" "^1.0.7"
     "@tsconfig/node10" "^1.0.7"
     "@tsconfig/node12" "^1.0.7"
     "@tsconfig/node12" "^1.0.7"
@@ -4603,29 +4654,29 @@ [email protected]:
   resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz#1a3407c158d547a9feb4229a9e3385b7b60c9917"
   resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz#1a3407c158d547a9feb4229a9e3385b7b60c9917"
   integrity sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==
   integrity sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==
 
 
-webpack-sources@^2.3.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.0.tgz#9ed2de69b25143a4c18847586ad9eccb19278cfa"
-  integrity sha512-WyOdtwSvOML1kbgtXbTDnEW0jkJ7hZr/bDByIwszhWd/4XX1A3XMkrbFMsuH4+/MfLlZCUzlAdg4r7jaGKEIgQ==
+webpack-sources@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd"
+  integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==
   dependencies:
   dependencies:
     source-list-map "^2.0.1"
     source-list-map "^2.0.1"
     source-map "^0.6.1"
     source-map "^0.6.1"
 
 
[email protected]2.0:
-  version "5.42.0"
-  resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.42.0.tgz#39aadbce84ad2cebf86cc5f88a2c53db65cbddfb"
-  integrity sha512-Ln8HL0F831t1x/yPB/qZEUVmZM4w9BnHZ1EQD/sAUHv8m22hthoPniWTXEzFMh/Sf84mhrahut22TX5KxWGuyQ==
[email protected]6.0:
+  version "5.46.0"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.46.0.tgz#105d20d96f79db59b316b0ae54316f0f630314b5"
+  integrity sha512-qxD0t/KTedJbpcXUmvMxY5PUvXDbF8LsThCzqomeGaDlCA6k998D8yYVwZMvO8sSM3BTEOaD4uzFniwpHaTIJw==
   dependencies:
   dependencies:
     "@types/eslint-scope" "^3.7.0"
     "@types/eslint-scope" "^3.7.0"
-    "@types/estree" "^0.0.48"
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/wasm-edit" "1.11.0"
-    "@webassemblyjs/wasm-parser" "1.11.0"
+    "@types/estree" "^0.0.50"
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/wasm-edit" "1.11.1"
+    "@webassemblyjs/wasm-parser" "1.11.1"
     acorn "^8.4.1"
     acorn "^8.4.1"
     browserslist "^4.14.5"
     browserslist "^4.14.5"
     chrome-trace-event "^1.0.2"
     chrome-trace-event "^1.0.2"
     enhanced-resolve "^5.8.0"
     enhanced-resolve "^5.8.0"
-    es-module-lexer "^0.6.0"
+    es-module-lexer "^0.7.1"
     eslint-scope "5.1.1"
     eslint-scope "5.1.1"
     events "^3.2.0"
     events "^3.2.0"
     glob-to-regexp "^0.4.1"
     glob-to-regexp "^0.4.1"
@@ -4634,11 +4685,11 @@ [email protected]:
     loader-runner "^4.2.0"
     loader-runner "^4.2.0"
     mime-types "^2.1.27"
     mime-types "^2.1.27"
     neo-async "^2.6.2"
     neo-async "^2.6.2"
-    schema-utils "^3.0.0"
+    schema-utils "^3.1.0"
     tapable "^2.1.1"
     tapable "^2.1.1"
     terser-webpack-plugin "^5.1.3"
     terser-webpack-plugin "^5.1.3"
     watchpack "^2.2.0"
     watchpack "^2.2.0"
-    webpack-sources "^2.3.0"
+    webpack-sources "^2.3.1"
 
 
 which-boxed-primitive@^1.0.2:
 which-boxed-primitive@^1.0.2:
   version "1.0.2"
   version "1.0.2"

部分文件因文件數量過多而無法顯示