Explorar el Código

Sync with dev

sanex hace 3 años
padre
commit
ba3e4d6897
Se han modificado 63 ficheros con 2375 adiciones y 1087 borrados
  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
 /test/benchmark/**/**
 *dockerfile
+/test*.js

+ 18 - 1
CHANGELOG.md

@@ -1,9 +1,26 @@
 Change Log
 
-v2.16.0
+v2.18.0
 ---
 * 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
 ---
 * 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
 * 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
-* (Bitcoin) 1Nv2773RDNzodHDxuxaYkTvwBkYRHmPhnG
+* (Bitcoin) bc1q203p8nyrstwm7vwzjg3h9l9t6y9ka0umw0rx96
 
 Huge thanks to all supporters!
 
@@ -377,6 +377,7 @@ Following options are available for the JS Obfuscator:
     sourceMapBaseUrl: '',
     sourceMapFileName: '',
     sourceMapMode: 'separate',
+    sourceMapSourcesMode: 'sources-content',
     splitStrings: false,
     splitStringsChunkLength: 10,
     stringArray: true,
@@ -438,6 +439,7 @@ Following options are available for the JS Obfuscator:
     --source-map-base-url <string>
     --source-map-file-name <string>
     --source-map-mode <string> [inline, separate]
+    --source-map-sources-mode <string> [sources, sources-content]
     --split-strings <boolean>
     --split-strings-chunk-length <number>
     --string-array <boolean>
@@ -840,6 +842,7 @@ Prevents obfuscation of `require` imports. Could be helpful in some cases when f
 Type: `string` Default: `''`
 
 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`
 Type: `boolean` Default: `false`
@@ -1071,6 +1074,13 @@ Specifies source map generation mode:
 * `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`.
 
+### `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`
 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.
 
 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.
 
@@ -1304,9 +1314,6 @@ const a = [
     'bar',
     'foo'
 ];
-const d = function (c, g) {
-    return b(g - 0x3e1, c);
-};
 const foo = d(0x567, 0x568);
 function b(c, d) {
     b = function (e, f) {
@@ -1317,14 +1324,17 @@ function b(c, d) {
     return b(c, d);
 }
 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);
+    function e (c, g) {
+        return b(c - 0x396, g);
+    }
     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();
 ```

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/index.browser.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/index.cli.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/index.js


+ 20 - 19
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "2.16.0",
+  "version": "2.18.0",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",
@@ -30,7 +30,7 @@
     "chance": "1.1.7",
     "class-validator": "0.13.1",
     "commander": "8.0.0",
-    "eslint-scope": "github:eslint/eslint-scope#master",
+    "eslint-scope": "6.0.0",
     "fast-deep-equal": "3.1.3",
     "inversify": "5.1.1",
     "js-string-escape": "1.0.1",
@@ -46,30 +46,30 @@
   },
   "devDependencies": {
     "@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/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/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/string-template": "1.0.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-exclude": "2.0.3",
     "cross-env": "7.0.3",
-    "eslint": "7.30.0",
+    "eslint": "7.31.0",
     "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-prefer-arrow": "1.2.3",
     "eslint-plugin-unicorn": "34.0.1",
@@ -81,11 +81,12 @@
     "pre-commit": "1.2.2",
     "rimraf": "3.0.2",
     "sinon": "11.1.1",
+    "source-map-resolve": "^0.6.0",
     "threads": "1.6.5",
     "ts-loader": "9.2.3",
-    "ts-node": "10.0.0",
+    "ts-node": "10.1.0",
     "typescript": "4.4.0-beta",
-    "webpack": "5.42.0",
+    "webpack": "5.46.0",
     "webpack-cli": "4.7.2",
     "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 { NodeGuards } from './node/NodeGuards';
 import { Utils } from './utils/Utils';
+import { SourceMapSourcesMode } from './enums/source-map/SourceMapSourcesMode';
 
 @injectable()
 export class JavaScriptObfuscator implements IJavaScriptObfuscator {
@@ -247,20 +248,23 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
      */
     private generateCode (sourceCode: string, astTree: ESTree.Program): IGeneratorOutput {
         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: {
                 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() : '';
 

+ 8 - 1
src/cli/JavaScriptObfuscatorCLI.ts

@@ -17,6 +17,7 @@ import { ObfuscationTarget } from '../enums/ObfuscationTarget';
 import { OptionsPreset } from '../enums/options/presets/OptionsPreset';
 import { RenamePropertiesMode } from '../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 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 { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 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';
 
     /**
-     * @type {commander.CommanderStatic}
+     * @type {commander.Command}
      */
     @initializable()
     private commands!: commander.Command;
@@ -352,6 +353,12 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 `Values: ${CLIUtils.stringifyOptionAvailableValues(SourceMapMode)}. ` +
                 `Default: ${SourceMapMode.Separate}`
             )
+            .option(
+                '--source-map-sources-mode <string>',
+                'Specify source map sources mode. ' +
+                `Values: ${CLIUtils.stringifyOptionAvailableValues(SourceMapSourcesMode)}. ` +
+                `Default: ${SourceMapSourcesMode.SourcesContent}`
+            )
             .option(
                 '--split-strings <boolean>',
                 '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) {
             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
             const normalizedSourceMapFilePath: string = sourceMapFileName.replace(/^[a-zA-Z]:\\*/, '');
 

+ 1 - 2
src/container/ServiceIdentifiers.ts

@@ -53,8 +53,7 @@ export enum ServiceIdentifiers {
     ISourceCode = 'ISourceCode',
     IScopeAnalyzer = 'IScopeAnalyzer',
     IStringArrayIndexNode = 'IStringArrayIndexNode',
-    IStringArrayScopeCallsWrapperLexicalScopeDataStorage = 'IStringArrayScopeCallsWrapperLexicalScopeDataStorage',
-    IStringArrayScopeCallsWrapperNamesDataStorage = 'IStringArrayScopeCallsWrapperNamesDataStorage',
+    IStringArrayScopeCallsWrappersDataStorage = 'IStringArrayScopeCallsWrappersDataStorage',
     IStringArrayStorage = 'IStringArrayStorage',
     IStringArrayStorageAnalyzer = 'IStringArrayStorageAnalyzer',
     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 { IPropertyIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage';
 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 { 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 { LiteralNodesCacheStorage } from '../../../storages/string-array-transformers/LiteralNodesCacheStorage';
 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 { VisitedLexicalScopeNodesStackStorage } from '../../../storages/string-array-transformers/VisitedLexicalScopeNodesStackStorage';
 
@@ -46,12 +44,8 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b
         .to(StringArrayStorage)
         .inSingletonScope();
 
-    bind<IStringArrayScopeCallsWrapperLexicalScopeDataStorage>(ServiceIdentifiers.IStringArrayScopeCallsWrapperLexicalScopeDataStorage)
-        .to(StringArrayScopeCallsWrapperLexicalScopeDataStorage)
-        .inSingletonScope();
-
-    bind<IStringArrayScopeCallsWrapperNamesDataStorage>(ServiceIdentifiers.IStringArrayScopeCallsWrapperNamesDataStorage)
-        .to(StringArrayScopeCallsWrapperNamesDataStorage)
+    bind<IStringArrayScopeCallsWrappersDataStorage>(ServiceIdentifiers.IStringArrayScopeCallsWrappersDataStorage)
+        .to(StringArrayScopeCallsWrappersDataStorage)
         .inSingletonScope();
 
     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 { IOptions } from '../../interfaces/options/IOptions';
 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 { initializable } from '../../decorators/Initializable';
@@ -19,6 +18,7 @@ import { initializable } from '../../decorators/Initializable';
 import { AbstractStringArrayCallNode } from './AbstractStringArrayCallNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeUtils } from '../../node/NodeUtils';
+import { IStringArrayScopeCallsWrapperData } from '../../interfaces/node-transformers/string-array-transformers/IStringArrayScopeCallsWrapperData';
 
 @injectable()
 export class StringArrayCallNode extends AbstractStringArrayCallNode {
@@ -41,16 +41,10 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
     private indexShiftAmount!: number;
 
     /**
-     * @type {string}
+     * @type {IStringArrayScopeCallsWrapperData}
      */
     @initializable()
-    private stringArrayCallsWrapperName!: string;
-
-    /**
-     * @type {IStringArrayScopeCallsWrapperParameterIndexesData | null}
-     */
-    @initializable()
-    private stringArrayCallsWrapperParameterIndexesData!: IStringArrayScopeCallsWrapperParameterIndexesData | null;
+    private stringArrayCallsWrapperData!: IStringArrayScopeCallsWrapperData;
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
@@ -84,23 +78,20 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
     }
 
     /**
-     * @param {string} stringArrayCallsWrapperName
-     * @param {IStringArrayScopeCallsWrapperParameterIndexesData | null} stringArrayCallsWrapperParameterIndexesData
      * @param {number} index
      * @param {number} indexShiftAmount
+     * @param {IStringArrayScopeCallsWrapperData} stringArrayCallsWrapperData
      * @param {string | null} decodeKey
      */
     public initialize (
-        stringArrayCallsWrapperName: string,
-        stringArrayCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null,
         index: number,
         indexShiftAmount: number,
+        stringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData,
         decodeKey: string | null
     ): void {
-        this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
-        this.stringArrayCallsWrapperParameterIndexesData = stringArrayCallsWrapperParameterIndexesData;
         this.index = index;
         this.indexShiftAmount = indexShiftAmount;
+        this.stringArrayCallsWrapperData = stringArrayCallsWrapperData;
         this.decodeKey = decodeKey;
     }
 
@@ -108,7 +99,7 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
      * @returns {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 rc4KeyLiteralNode: ESTree.Literal | null = this.decodeKey
@@ -117,7 +108,7 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
 
         // filling call expression arguments with a fake arguments first
         const callExpressionArgs: ESTree.Expression[] = this.arrayUtils.fillWithRange(
-            !this.stringArrayCallsWrapperParameterIndexesData
+            !this.stringArrayCallsWrapperData.parameterIndexesData
                 // root string array calls wrapper
                 ? AbstractStringArrayCallNode.stringArrayRootCallsWrapperParametersCount
                 // scope string array calls wrapper
@@ -126,14 +117,14 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
         );
 
         callExpressionArgs.splice(
-            this.stringArrayCallsWrapperParameterIndexesData?.valueIndexParameterIndex ?? 0,
+            this.stringArrayCallsWrapperData.parameterIndexesData?.valueIndexParameterIndex ?? 0,
             1,
             indexNode
         );
 
-        if (this.stringArrayCallsWrapperParameterIndexesData) {
+        if (this.stringArrayCallsWrapperData.parameterIndexesData) {
             callExpressionArgs.splice(
-                this.stringArrayCallsWrapperParameterIndexesData.decodeKeyParameterIndex,
+                this.stringArrayCallsWrapperData.parameterIndexesData.decodeKeyParameterIndex,
                 1,
                 // use rc4 key literal node if exists or a node with fake string array wrapper index
                 rc4KeyLiteralNode ?? this.getFakeStringArrayIndexNode(resultIndex)
@@ -147,7 +138,7 @@ export class StringArrayCallNode extends AbstractStringArrayCallNode {
 
         const structure: TStatement = NodeFactory.expressionStatementNode(
             NodeFactory.callExpressionNode(
-                NodeFactory.identifierNode(this.stringArrayCallsWrapperName),
+                NodeFactory.identifierNode(this.stringArrayCallsWrapperData.name),
                 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 { IOptions } from '../../interfaces/options/IOptions';
 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 { initializable } from '../../decorators/Initializable';
@@ -23,35 +23,16 @@ import { NodeUtils } from '../../node/NodeUtils';
 @injectable()
 export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArrayCallNode {
     /**
-     * @type {number}
+     * @type {IStringArrayScopeCallsWrapperData}
      */
     @initializable()
-    private shiftedIndex!: number;
+    private upperStringArrayCallsWrapperData!: IStringArrayScopeCallsWrapperData;
 
     /**
-     * @type {string}
+     * @type {IStringArrayScopeCallsWrapperData}
      */
     @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
@@ -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 (
-        stringArrayScopeCallsWrapperName: string,
-        stringArrayScopeCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null,
-        upperStringArrayCallsWrapperName: string,
-        upperStringArrayCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null,
-        shiftedIndex: number,
+        stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData,
+        upperStringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData,
     ): 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(
             stringArrayCallIdentifierNode,
-            this.getStringArrayIndexNode(this.shiftedIndex)
+            this.getStringArrayIndexNode(
+                this.stringArrayScopeCallsWrapperData.index
+                - this.upperStringArrayCallsWrapperData.index
+            )
         );
 
         // stage 1: function expression node parameters
         // filling all parameters with a fake parameters first
         const parameters: ESTree.Identifier[] = this.arrayUtils.fillWithRange(
-            !this.stringArrayScopeCallsWrapperParameterIndexesData
+            !this.stringArrayScopeCallsWrapperData.parameterIndexesData
                 // root string array calls wrapper
                 ? AbstractStringArrayCallNode.stringArrayRootCallsWrapperParametersCount
                 // scope string array calls wrapper
@@ -130,12 +105,12 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
             () => this.getFakeParameterNode()
         );
         parameters.splice(
-            this.stringArrayScopeCallsWrapperParameterIndexesData?.valueIndexParameterIndex ?? 0,
+            this.stringArrayScopeCallsWrapperData.parameterIndexesData?.valueIndexParameterIndex ?? 0,
             1,
             stringArrayCallIdentifierNode
         );
         parameters.splice(
-            this.stringArrayScopeCallsWrapperParameterIndexesData?.decodeKeyParameterIndex ?? 1,
+            this.stringArrayScopeCallsWrapperData.parameterIndexesData?.decodeKeyParameterIndex ?? 1,
             1,
             decodeKeyIdentifierNode
         );
@@ -143,7 +118,7 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
         // stage 2: upper string array call expression arguments
         // filling all call expression arguments with a fake string array calls
         const callExpressionArgs: ESTree.Expression[] = this.arrayUtils.fillWithRange(
-            !this.upperStringArrayCallsWrapperParameterIndexesData
+            !this.upperStringArrayCallsWrapperData.parameterIndexesData
                 // root string array calls wrapper
                 ? AbstractStringArrayCallNode.stringArrayRootCallsWrapperParametersCount
                 // scope string array calls wrapper
@@ -155,45 +130,38 @@ export class StringArrayScopeCallsWrapperFunctionNode extends AbstractStringArra
         );
 
         callExpressionArgs.splice(
-            this.upperStringArrayCallsWrapperParameterIndexesData?.valueIndexParameterIndex ?? 0,
+            this.upperStringArrayCallsWrapperData.parameterIndexesData?.valueIndexParameterIndex ?? 0,
             1,
             stringArrayCallNode
         );
         callExpressionArgs.splice(
-            this.upperStringArrayCallsWrapperParameterIndexesData?.decodeKeyParameterIndex ?? 1,
+            this.upperStringArrayCallsWrapperData.parameterIndexesData?.decodeKeyParameterIndex ?? 1,
             1,
             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,
             NodeFactory.blockStatementNode([
                 NodeFactory.returnStatementNode(
                     NodeFactory.callExpressionNode(
-                        NodeFactory.identifierNode(this.upperStringArrayCallsWrapperName),
+                        NodeFactory.identifierNode(this.upperStringArrayCallsWrapperData.name),
                         callExpressionArgs
                     )
                 )
             ])
         );
 
-        const structure: TStatement = NodeFactory.variableDeclarationNode(
-            [
-                NodeFactory.variableDeclaratorNode(
-                    NodeFactory.identifierNode(this.stringArrayScopeCallsWrapperName),
-                    functionExpressionNode
-                )
-            ],
-            'const',
-        );
+        const structure: TStatement = functionDeclarationNode;
 
         NodeUtils.parentizeAst(structure);
 
         // stage 4: rename
         // have to generate names for both parameter and call identifiers
         for (const parameter of parameters) {
-            parameter.name = this.identifierNamesGenerator.generateForLexicalScope(functionExpressionNode);
+            parameter.name = this.identifierNamesGenerator.generateForLexicalScope(functionDeclarationNode);
         }
 
         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 { IOptions } from '../../interfaces/options/IOptions';
 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 { initializable } from '../../decorators/Initializable';
@@ -20,16 +21,16 @@ import { NodeUtils } from '../../node/NodeUtils';
 @injectable()
 export class StringArrayScopeCallsWrapperVariableNode extends AbstractStringArrayCallNode {
     /**
-     * @type {string}
+     * @type {IStringArrayScopeCallsWrapperData}
      */
     @initializable()
-    private stringArrayCallsWrapperName!: string;
+    private stringArrayCallsWrapperData!: IStringArrayScopeCallsWrapperData;
 
     /**
-     * @type {string}
+     * @type {IStringArrayScopeCallsWrapperData}
      */
     @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 (
-        stringArrayScopeCallsWrapperName: string,
-        stringArrayCallsWrapperName: string
+        stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData,
+        stringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData
     ): 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(
             [
                 NodeFactory.variableDeclaratorNode(
-                    NodeFactory.identifierNode(this.stringArrayScopeCallsWrapperName),
-                    NodeFactory.identifierNode(this.stringArrayCallsWrapperName)
+                    NodeFactory.identifierNode(this.stringArrayScopeCallsWrapperData.name),
+                    NodeFactory.identifierNode(this.stringArrayCallsWrapperData.name)
                 )
             ],
             'const',

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

@@ -35,6 +35,7 @@ export enum NodeType {
     LogicalExpression = 'LogicalExpression',
     MemberExpression = 'MemberExpression',
     MethodDefinition = 'MethodDefinition',
+    NewExpression = 'NewExpression',
     ObjectExpression = 'ObjectExpression',
     ObjectPattern = 'ObjectPattern',
     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';
 
-export interface IStringArrayScopeCallsWrapperNamesData {
+import { IStringArrayScopeCallsWrapperData } from './IStringArrayScopeCallsWrapperData';
+
+export interface IStringArrayScopeCallsWrappersData {
     /**
      * @type {TStringArrayEncoding}
      */
@@ -9,5 +11,5 @@ export interface IStringArrayScopeCallsWrapperNamesData {
     /**
      * @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 { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
+import { SourceMapSourcesMode } from '../../enums/source-map/SourceMapSourcesMode';
 
 export interface IOptions {
     readonly compact: boolean;
@@ -45,6 +46,7 @@ export interface IOptions {
     readonly sourceMapBaseUrl: string;
     readonly sourceMapFileName: string;
     readonly sourceMapMode: TTypeFromEnum<typeof SourceMapMode>;
+    readonly sourceMapSourcesMode: TTypeFromEnum<typeof SourceMapSourcesMode>;
     readonly splitStrings: boolean;
     readonly splitStringsChunkLength: number;
     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,
                 objectExpressionParentNode
             )
+            || ObjectExpressionKeysTransformer.isObjectExpressionWithCallExpression(
+                objectExpressionNode,
+            )
             || ObjectExpressionKeysTransformer.isProhibitedSequenceExpression(
                 objectExpressionNode,
                 objectExpressionHostStatement
@@ -154,6 +157,31 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
             && 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 {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 {
         if (this.isReservedName(propertyName)) {
+            this.identifierNamesGenerator.preserveName(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 { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 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 { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 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 { IVisitedLexicalScopeNodesStackStorage } from '../../interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
@@ -48,14 +45,9 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     private readonly stringArrayStorage: IStringArrayStorage;
 
     /**
-     * @type {IStringArrayScopeCallsWrapperLexicalScopeDataStorage}
+     * @type {IStringArrayScopeCallsWrappersDataStorage}
      */
-    private readonly stringArrayScopeCallsWrapperLexicalScopeDataStorage: IStringArrayScopeCallsWrapperLexicalScopeDataStorage;
-
-    /**
-     * @type {IStringArrayScopeCallsWrapperNamesDataStorage}
-     */
-    private readonly stringArrayScopeCallsWrapperNamesDataStorage: IStringArrayScopeCallsWrapperNamesDataStorage;
+    private readonly stringArrayScopeCallsWrappersDataStorage: IStringArrayScopeCallsWrappersDataStorage;
 
     /**
      * @type {TStringArrayCustomNodeFactory}
@@ -72,8 +64,7 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
      * @param {IOptions} options
      * @param {IVisitedLexicalScopeNodesStackStorage} visitedLexicalScopeNodesStackStorage
      * @param {IStringArrayStorage} stringArrayStorage
-     * @param {IStringArrayScopeCallsWrapperNamesDataStorage} stringArrayScopeCallsWrapperNamesDataStorage
-     * @param {IStringArrayScopeCallsWrapperLexicalScopeDataStorage} stringArrayScopeCallsWrapperLexicalScopeDataStorage
+     * @param {IStringArrayScopeCallsWrappersDataStorage} stringArrayScopeCallsWrappersDataStorage
      * @param {TStringArrayCustomNodeFactory} stringArrayTransformerCustomNodeFactory
      */
     public constructor (
@@ -81,8 +72,7 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
         @inject(ServiceIdentifiers.IOptions) options: IOptions,
         @inject(ServiceIdentifiers.IVisitedLexicalScopeNodesStackStorage) visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage,
         @inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
-        @inject(ServiceIdentifiers.IStringArrayScopeCallsWrapperNamesDataStorage) stringArrayScopeCallsWrapperNamesDataStorage: IStringArrayScopeCallsWrapperNamesDataStorage,
-        @inject(ServiceIdentifiers.IStringArrayScopeCallsWrapperLexicalScopeDataStorage) stringArrayScopeCallsWrapperLexicalScopeDataStorage: IStringArrayScopeCallsWrapperLexicalScopeDataStorage,
+        @inject(ServiceIdentifiers.IStringArrayScopeCallsWrappersDataStorage) stringArrayScopeCallsWrappersDataStorage: IStringArrayScopeCallsWrappersDataStorage,
         @inject(ServiceIdentifiers.Factory__IStringArrayCustomNode)
             stringArrayTransformerCustomNodeFactory: TStringArrayCustomNodeFactory
     ) {
@@ -90,8 +80,7 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
 
         this.visitedLexicalScopeNodesStackStorage = visitedLexicalScopeNodesStackStorage;
         this.stringArrayStorage = stringArrayStorage;
-        this.stringArrayScopeCallsWrapperNamesDataStorage = stringArrayScopeCallsWrapperNamesDataStorage;
-        this.stringArrayScopeCallsWrapperLexicalScopeDataStorage = stringArrayScopeCallsWrapperLexicalScopeDataStorage;
+        this.stringArrayScopeCallsWrappersDataStorage = stringArrayScopeCallsWrappersDataStorage;
         this.stringArrayTransformerCustomNodeFactory = stringArrayTransformerCustomNodeFactory;
     }
 
@@ -133,56 +122,38 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     public transformNode (
         lexicalScopeBodyNode: 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;
         }
 
-        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
-        for (const stringArrayScopeCallsWrapperNamesData of stringArrayScopeCallsWrapperNamesDataList) {
-            if (!stringArrayScopeCallsWrapperNamesData) {
+        for (const stringArrayScopeCallsWrappersData of stringArrayScopeCallsWrappersDataList) {
+            if (!stringArrayScopeCallsWrappersData) {
                 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
              * 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,
-                    stringArrayScopeCallsWrapperNode
+                    stringArrayScopeCallsWrapperData,
+                    upperStringArrayCallsWrapperData
                 );
             }
         }
@@ -191,40 +162,32 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
     }
 
     /**
-     * @param {IStringArrayScopeCallsWrapperNamesData} stringArrayScopeCallsWrapperNamesData
-     * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperLexicalScopeData
+     * @param {IStringArrayScopeCallsWrappersData} stringArrayScopeCallsWrappersData
      * @returns {IStringArrayScopeCallsWrapperData}
      */
     private getRootStringArrayCallsWrapperData (
-        stringArrayScopeCallsWrapperNamesData: IStringArrayScopeCallsWrapperNamesData,
-        stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
+        stringArrayScopeCallsWrappersData: IStringArrayScopeCallsWrappersData,
     ): IStringArrayScopeCallsWrapperData {
-        const {encoding} = stringArrayScopeCallsWrapperNamesData;
-        const {resultShiftedIndex} = stringArrayScopeCallsWrapperLexicalScopeData;
+        const {encoding} = stringArrayScopeCallsWrappersData;
 
         return {
             name: this.stringArrayStorage.getStorageCallsWrapperName(encoding),
-            index: resultShiftedIndex,
+            index: 0,
             parameterIndexesData: null
         };
     }
 
     /**
-     * @param {IStringArrayScopeCallsWrapperNamesData} stringArrayScopeCallsWrapperNamesData
-     * @param {IStringArrayScopeCallsWrapperLexicalScopeData} stringArrayScopeCallsWrapperLexicalScopeData
+     * @param {IStringArrayScopeCallsWrappersData} stringArrayScopeCallsWrappersData
      * @returns {IStringArrayScopeCallsWrapperData}
      */
     private getUpperStringArrayCallsWrapperData (
-        stringArrayScopeCallsWrapperNamesData: IStringArrayScopeCallsWrapperNamesData,
-        stringArrayScopeCallsWrapperLexicalScopeData: IStringArrayScopeCallsWrapperLexicalScopeData
+        stringArrayScopeCallsWrappersData: IStringArrayScopeCallsWrappersData,
     ): 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) {
             return rootStringArrayCallsWrapperData;
@@ -238,72 +201,77 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
             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;
         }
 
-        const upperStringArrayCallsWrapperName: string = this.randomGenerator
+        return this.randomGenerator
             .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) {
-            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:
-            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[]}
      */
     private getStringArrayScopeCallsWrapperVariableNode (
-        stringArrayScopeCallsWrapperName: string,
-        upperStringArrayCallsWrapperName: string
+        stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData,
+        upperStringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData
     ): TStatement[] {
         const stringArrayScopeCallsWrapperVariableNode: ICustomNode<TInitialData<StringArrayScopeCallsWrapperVariableNode>> =
             this.stringArrayTransformerCustomNodeFactory(
@@ -311,27 +279,21 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
             );
 
         stringArrayScopeCallsWrapperVariableNode.initialize(
-            stringArrayScopeCallsWrapperName,
-            upperStringArrayCallsWrapperName
+            stringArrayScopeCallsWrapperData,
+            upperStringArrayCallsWrapperData
         );
 
         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[]}
      */
     private getStringArrayScopeCallsWrapperFunctionNode (
-        stringArrayScopeCallsWrapperName: string,
-        stringArrayScopeCallsWrapperParameterIndexes: IStringArrayScopeCallsWrapperParameterIndexesData | null,
-        upperStringArrayCallsWrapperName: string,
-        upperStringArrayCallsWrapperParameterIndexes: IStringArrayScopeCallsWrapperParameterIndexesData | null,
-        stringArrayScopeCallsWrapperShiftedIndex: number
+        stringArrayScopeCallsWrapperData: IStringArrayScopeCallsWrapperData,
+        upperStringArrayCallsWrapperData: IStringArrayScopeCallsWrapperData,
     ): TStatement[] {
         const stringArrayScopeCallsWrapperFunctionNode: ICustomNode<TInitialData<StringArrayScopeCallsWrapperFunctionNode>> =
             this.stringArrayTransformerCustomNodeFactory(
@@ -339,11 +301,8 @@ export class StringArrayScopeCallsWrapperTransformer extends AbstractNodeTransfo
             );
 
         stringArrayScopeCallsWrapperFunctionNode.initialize(
-            stringArrayScopeCallsWrapperName,
-            stringArrayScopeCallsWrapperParameterIndexes,
-            upperStringArrayCallsWrapperName,
-            upperStringArrayCallsWrapperParameterIndexes,
-            stringArrayScopeCallsWrapperShiftedIndex,
+            stringArrayScopeCallsWrapperData,
+            upperStringArrayCallsWrapperData
         );
 
         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 { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
 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 { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
@@ -16,9 +16,7 @@ import { ILiteralNodesCacheStorage } from '../../interfaces/storages/string-arra
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 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 { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
 import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
@@ -57,7 +55,6 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
         NodeTransformer.StringArrayRotateFunctionTransformer
     ];
 
-
     /**
      * @type {IIdentifierNamesGenerator}
      */
@@ -79,14 +76,9 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
     private readonly stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer;
 
     /**
-     * @type {IStringArrayScopeCallsWrapperLexicalScopeDataStorage}
-     */
-    private readonly stringArrayScopeCallsWrapperLexicalScopeDataStorage: IStringArrayScopeCallsWrapperLexicalScopeDataStorage;
-
-    /**
-     * @type {IStringArrayScopeCallsWrapperNamesDataStorage}
+     * @type {IStringArrayScopeCallsWrappersDataStorage}
      */
-    private readonly stringArrayScopeCallsWrapperNamesDataStorage: IStringArrayScopeCallsWrapperNamesDataStorage;
+    private readonly stringArrayScopeCallsWrappersDataStorage: IStringArrayScopeCallsWrappersDataStorage;
 
     /**
      * @type {TStringArrayCustomNodeFactory}
@@ -104,8 +96,7 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
      * @param {ILiteralNodesCacheStorage} literalNodesCacheStorage
      * @param {IVisitedLexicalScopeNodesStackStorage} visitedLexicalScopeNodesStackStorage
      * @param {IStringArrayStorage} stringArrayStorage
-     * @param {IStringArrayScopeCallsWrapperNamesDataStorage} stringArrayScopeCallsWrapperNamesDataStorage
-     * @param {IStringArrayScopeCallsWrapperLexicalScopeDataStorage} stringArrayScopeCallsWrapperLexicalScopeDataStorage
+     * @param {IStringArrayScopeCallsWrappersDataStorage} stringArrayScopeCallsWrappersDataStorage
      * @param {IStringArrayStorageAnalyzer} stringArrayStorageAnalyzer
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {TStringArrayCustomNodeFactory} stringArrayTransformerCustomNodeFactory
@@ -116,10 +107,8 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
         @inject(ServiceIdentifiers.ILiteralNodesCacheStorage) literalNodesCacheStorage: ILiteralNodesCacheStorage,
         @inject(ServiceIdentifiers.IVisitedLexicalScopeNodesStackStorage) visitedLexicalScopeNodesStackStorage: IVisitedLexicalScopeNodesStackStorage,
         @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.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
@@ -131,8 +120,7 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
         this.literalNodesCacheStorage = literalNodesCacheStorage;
         this.visitedLexicalScopeNodesStackStorage = visitedLexicalScopeNodesStackStorage;
         this.stringArrayStorage = stringArrayStorage;
-        this.stringArrayScopeCallsWrapperNamesDataStorage = stringArrayScopeCallsWrapperNamesDataStorage;
-        this.stringArrayScopeCallsWrapperLexicalScopeDataStorage = stringArrayScopeCallsWrapperLexicalScopeDataStorage;
+        this.stringArrayScopeCallsWrappersDataStorage = stringArrayScopeCallsWrappersDataStorage;
         this.stringArrayStorageAnalyzer = stringArrayStorageAnalyzer;
         this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
         this.stringArrayTransformerCustomNodeFactory = stringArrayTransformerCustomNodeFactory;
@@ -219,21 +207,17 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
      * @returns {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>> =
             this.stringArrayTransformerCustomNodeFactory(StringArrayCustomNode.StringArrayCallNode);
 
         stringArrayCallCustomNode.initialize(
-            stringArrayCallsWrapperName,
-            parameterIndexesData,
             index,
             this.stringArrayStorage.getIndexShiftAmount(),
+            stringArrayScopeCallsWrapperData,
             decodeKey
         );
 
@@ -250,29 +234,29 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
      * @returns {IStringArrayScopeCallsWrapperData}
      */
-    private getStringArrayCallsWrapperData (
+    private getStringArrayScopeCallsWrapperData (
         stringArrayStorageItemData: IStringArrayStorageItemData
     ): IStringArrayScopeCallsWrapperData {
         return !this.options.stringArrayWrappersCount
-            ? this.getRootStringArrayCallsWrapperData(stringArrayStorageItemData)
-            : this.getUpperStringArrayCallsWrapperData(stringArrayStorageItemData);
+            ? this.getRootStringArrayScopeCallsWrapperData(stringArrayStorageItemData)
+            : this.getUpperStringArrayScopeCallsWrapperData(stringArrayStorageItemData);
     }
 
     /**
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
      * @returns {IStringArrayScopeCallsWrapperData}
      */
-    private getRootStringArrayCallsWrapperData (
+    private getRootStringArrayScopeCallsWrapperData (
         stringArrayStorageItemData: IStringArrayStorageItemData
     ): IStringArrayScopeCallsWrapperData {
-        const {encoding, index} = stringArrayStorageItemData;
+        const {encoding} = stringArrayStorageItemData;
 
         const rootStringArrayCallsWrapperName: string = this.stringArrayStorage.getStorageCallsWrapperName(encoding);
 
         return {
             name: rootStringArrayCallsWrapperName,
-            parameterIndexesData: null,
-            index
+            index: 0,
+            parameterIndexesData: null
         };
     }
 
@@ -280,143 +264,103 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
      * @param {IStringArrayStorageItemData} stringArrayStorageItemData
      * @returns {IStringArrayScopeCallsWrapperData}
      */
-    private getUpperStringArrayCallsWrapperData (
+    private getUpperStringArrayScopeCallsWrapperData (
         stringArrayStorageItemData: IStringArrayStorageItemData
     ): IStringArrayScopeCallsWrapperData {
-        const {encoding, index} = stringArrayStorageItemData;
+        const {encoding} = stringArrayStorageItemData;
         const currentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null =
             this.visitedLexicalScopeNodesStackStorage.getLastElement() ?? null;
-        const parentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements | null =
-            this.visitedLexicalScopeNodesStackStorage.getPenultimateElement() ?? null;
 
         if (!currentLexicalScopeBodyNode) {
             throw new Error('Cannot find current lexical scope body node');
         }
 
-        const stringArrayScopeCallsWrapperNamesDataByEncoding: TStringArrayScopeCallsWrapperNamesDataByEncoding =
-            this.getAndUpdateStringArrayScopeCallsWrapperNamesDataByEncoding(
+        const stringArrayScopeCallsWrappersDataByEncoding: TStringArrayScopeCallsWrappersDataByEncoding =
+            this.getAndUpdateStringArrayScopeCallsWrappersDataByEncoding(
                 currentLexicalScopeBodyNode,
                 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 {IStringArrayStorageItemData} stringArrayStorageItemData
-     * @returns {TStringArrayScopeCallsWrapperNamesDataByEncoding}
+     * @returns {TStringArrayScopeCallsWrappersDataByEncoding}
      */
-    private getAndUpdateStringArrayScopeCallsWrapperNamesDataByEncoding (
+    private getAndUpdateStringArrayScopeCallsWrappersDataByEncoding (
         currentLexicalScopeBodyNode: TNodeWithLexicalScopeStatements,
-        stringArrayStorageItemData: IStringArrayStorageItemData
-    ): TStringArrayScopeCallsWrapperNamesDataByEncoding {
+        stringArrayStorageItemData: IStringArrayStorageItemData,
+    ): TStringArrayScopeCallsWrappersDataByEncoding {
         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) {
-            return stringArrayScopeCallsWrapperNamesDataByEncoding;
+            return stringArrayScopeCallsWrappersDataByEncoding;
         }
 
         // have to use `generateForGlobalScope` for program node for correct attach prefix to the calls wrapper name
         const nextScopeCallsWrapperName: string = NodeGuards.isProgramNode(currentLexicalScopeBodyNode)
             ? this.identifierNamesGenerator.generateForGlobalScope()
             : this.identifierNamesGenerator.generateNext();
+        const nextScopeCallsWrapperShiftedIndex: number =
+            this.getStringArrayCallsWrapperShiftedIndex();
+        const nextScopeCallsWrapperParameterIndexesData: IStringArrayScopeCallsWrapperParameterIndexesData | null =
+            this.getStringArrayCallsWrapperParameterIndexesData();
 
-        stringArrayScopeCallsWrapperNamesDataByEncoding[encoding] = {
+        stringArrayScopeCallsWrappersDataByEncoding[encoding] = {
             encoding,
-            names: [
-                ...stringArrayScopeCallsWrapperNames,
-                nextScopeCallsWrapperName
+            scopeCallsWrappersData: [
+                ...stringArrayScopeCallsWrappersData,
+                {
+                    name: nextScopeCallsWrapperName,
+                    index: nextScopeCallsWrapperShiftedIndex,
+                    parameterIndexesData: nextScopeCallsWrapperParameterIndexesData
+                }
             ]
         };
 
-        this.stringArrayScopeCallsWrapperNamesDataStorage.set(
+        this.stringArrayScopeCallsWrappersDataStorage.set(
             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(
                 StringArrayTransformer.minShiftedIndexValue,
                 StringArrayTransformer.maxShiftedIndexValue
             )
             : 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 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;
     }
 
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    public static isConditionalExpressionNode (node: ESTree.Node): node is ESTree.ConditionalExpression {
+        return node.type === NodeType.ConditionalExpression;
+    }
+
     /**
      * @param {Node} node
      * @returns {boolean}
@@ -285,6 +293,14 @@ export class NodeGuards {
         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
      * @returns {boolean}
@@ -301,6 +317,14 @@ export class NodeGuards {
         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
      * @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 { RenamePropertiesMode } from '../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 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 { StringArrayEncoding } from '../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayWrappersType } from '../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
@@ -48,6 +49,7 @@ import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
 import { IsAllowedForObfuscationTargets } from './validators/IsAllowedForObfuscationTargets';
 import { IsDomainLockRedirectUrl } from './validators/IsDomainLockRedirectUrl';
 import { IsIdentifierNamesCache } from './validators/IsIdentifierNamesCache';
+import { IsInputFileName } from './validators/IsInputFileName';
 
 @injectable()
 export class Options implements IOptions {
@@ -194,7 +196,7 @@ export class Options implements IOptions {
     /**
      * @type {string}
      */
-    @IsString()
+    @IsInputFileName()
     public readonly inputFileName!: string;
 
     /**
@@ -312,6 +314,12 @@ export class Options implements IOptions {
     @IsIn([SourceMapMode.Inline, SourceMapMode.Separate])
     public readonly sourceMapMode!: TTypeFromEnum<typeof SourceMapMode>;
 
+    /**
+     * @type {SourceMapSourcesMode}
+     */
+    @IsIn([SourceMapSourcesMode.Sources, SourceMapSourcesMode.SourcesContent])
+    public readonly sourceMapSourcesMode!: TTypeFromEnum<typeof SourceMapSourcesMode>;
+
     /**
      * @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 { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 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 { StringArrayEncoding } from '../../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
@@ -46,6 +47,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     sourceMapBaseUrl: '',
     sourceMapFileName: '',
     sourceMapMode: SourceMapMode.Separate,
+    sourceMapSourcesMode: SourceMapSourcesMode.SourcesContent,
     splitStrings: false,
     splitStringsChunkLength: 10,
     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 { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 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 { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
@@ -42,6 +43,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
     sourceMapBaseUrl: '',
     sourceMapFileName: '',
     sourceMapMode: SourceMapMode.Separate,
+    sourceMapSourcesMode: SourceMapSourcesMode.SourcesContent,
     splitStrings: false,
     splitStringsChunkLength: 0,
     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 { 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 { 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';
 
 @injectable()
-export class StringArrayScopeCallsWrapperNamesDataStorage extends MapStorage <
+export class StringArrayScopeCallsWrappersDataStorage extends MapStorage <
     TNodeWithLexicalScopeStatements,
-    TStringArrayScopeCallsWrapperNamesDataByEncoding
-> implements IStringArrayScopeCallsWrapperNamesDataStorage {
+    TStringArrayScopeCallsWrappersDataByEncoding
+> implements IStringArrayScopeCallsWrappersDataStorage {
     /**
      * @param {IRandomGenerator} randomGenerator
      * @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/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 rimraf from 'rimraf';
 import * as sinon from 'sinon';
+import { resolveSources } from 'source-map-resolve';
 
 import { assert } from 'chai';
 
+import { ISourceMap } from '../../../src/interfaces/source-code/ISourceMap';
+
 import { StdoutWriteMock } from '../../mocks/StdoutWriteMock';
 
 import { JavaScriptObfuscatorCLI } from '../../../src/JavaScriptObfuscatorCLIFacade';
+import { parseSourceMapFromObfuscatedCode } from '../../helpers/parseSourceMapFromObfuscatedCode';
 
 describe('JavaScriptObfuscatorCLI', function (): void {
     this.timeout(100000);
@@ -530,12 +534,12 @@ describe('JavaScriptObfuscatorCLI', function (): void {
 
             describe('Variant #1: `--sourceMapMode` option value is `separate`', () => {
                 describe('Variant #1: default behaviour', () => {
-                    const expectedSourceMapSourceName: string = path.basename(fixtureFileName);
-
                     let isFileExist: boolean,
-                        sourceMapObject: any;
+                        resolvedSources: string,
+                        sourceCodeContent: string,
+                        sourceMapObject: ISourceMap;
 
-                    before(() => {
+                    before((done) => {
                         JavaScriptObfuscatorCLI.obfuscate([
                             'node',
                             'javascript-obfuscator',
@@ -551,10 +555,18 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         ]);
 
                         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;
-                            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) {
                             isFileExist = false;
                         }
@@ -568,18 +580,14 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         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`', () => {
                         assert.property(sourceMapObject, 'names');
                     });
 
+                    it('should resolve correct sources from source map', () => {
+                        assert.equal(resolvedSources, sourceCodeContent);
+                    });
+
                     after(() => {
                         rimraf.sync(outputFilePath);
                         rimraf.sync(outputSourceMapPath);
@@ -587,12 +595,12 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                 });
 
                 describe('Variant #2: `sourceMapBaseUrl` option is set', () => {
-                    const expectedSourceMapSourceName: string = path.basename(fixtureFileName);
-
                     let isFileExist: boolean,
-                        sourceMapObject: any;
+                        resolvedSources: string,
+                        sourceCodeContent: string,
+                        sourceMapObject: ISourceMap;
 
-                    before(() => {
+                    before((done) => {
                         JavaScriptObfuscatorCLI.obfuscate([
                             'node',
                             'javascript-obfuscator',
@@ -610,10 +618,18 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         ]);
 
                         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;
-                            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) {
                             isFileExist = false;
                         }
@@ -627,18 +643,14 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         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`', () => {
                         assert.property(sourceMapObject, 'names');
                     });
 
+                    it('should resolve correct sources from source map', () => {
+                        assert.equal(resolvedSources, sourceCodeContent);
+                    });
+
                     after(() => {
                         fs.unlinkSync(outputFilePath);
                         fs.unlinkSync(outputSourceMapPath);
@@ -646,15 +658,16 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                 });
 
                 describe('Variant #3: `--sourceMapFileName` option is set', () => {
-                    const expectedSourceMapSourceName: string = path.basename(fixtureFileName);
                     const sourceMapFileName: string = 'test';
                     const sourceMapFilePath: string = `${sourceMapFileName}.js.map`;
                     const outputSourceMapFilePath: string = path.join(outputDirName, sourceMapFilePath);
 
                     let isFileExist: boolean,
-                        sourceMapObject: any;
+                        resolvedSources: string,
+                        sourceCodeContent: string,
+                        sourceMapObject: ISourceMap;
 
-                    before(() => {
+                    before((done) => {
                         JavaScriptObfuscatorCLI.obfuscate([
                             'node',
                             'javascript-obfuscator',
@@ -672,10 +685,18 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         ]);
 
                         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;
-                            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) {
                             isFileExist = false;
                         }
@@ -689,54 +710,353 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         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`', () => {
                         assert.property(sourceMapObject, 'names');
                     });
 
+                    it('should resolve correct sources from source map', () => {
+                        assert.equal(resolvedSources, sourceCodeContent);
+                    });
+
                     after(() => {
                         fs.unlinkSync(outputFilePath);
                         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`', () => {
-                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 { resolveSources } from 'source-map-resolve';
 
 import { TDictionary } from '../../../src/types/TDictionary';
 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 { IObfuscationResult } from '../../../src/interfaces/source-code/IObfuscationResult';
+import { ISourceMap } from '../../../src/interfaces/source-code/ISourceMap';
 
 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 { StringArrayIndexesType } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayWrappersType } from '../../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
@@ -107,11 +111,13 @@ describe('JavaScriptObfuscator', () => {
 
         describe('`sourceMap` option is `true`', () => {
             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(
                         code,
                         {
@@ -121,26 +127,46 @@ describe('JavaScriptObfuscator', () => {
                     );
 
                     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', () => {
                     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`', () => {
                 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(
                         code,
                         {
@@ -151,7 +177,13 @@ describe('JavaScriptObfuscator', () => {
                     );
 
                     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', () => {
@@ -162,8 +194,21 @@ describe('JavaScriptObfuscator', () => {
                     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);
                 });
             });
+
+            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', () => {

+ 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);
             });
         });
+
+        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('transformNode', () => {
         describe('Mode: `unsafe`', () => {
-            describe('Hexadecimal identifier names generator', () => {
+            describe('Variant #1: Hexadecimal identifier names generator', () => {
                 describe('Variant #1: base properties rename', () => {
                     const property1RegExp: RegExp = /'(_0x[a-f0-9]{4,6})': *0x1/;
                     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', () => {
                     const property1RegExp: RegExp = /'a': *0x1/;
                     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', () => {
                     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`', () => {
@@ -406,6 +431,31 @@ describe('RenamePropertiesTransformer', () => {
                     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', () => {

+ 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 { checkCodeEvaluation } from '../../../../helpers/checkCodeEvaluation';
+import { getRegExpMatch } from '../../../../helpers/getRegExpMatch';
 
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
 
@@ -644,20 +645,29 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
 
             describe('Variant #1: base', () => {
                 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]\\);` +
-                        '};.*' +
+                        '}';
+
+                    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 bar *= *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;
                     });
 
-                    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', () => {
@@ -699,20 +717,29 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                 });
 
                 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]\\);` +
-                        '};.*' +
+                        '}';
+
+                    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 bar *= *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;
                     });
 
-                    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', () => {
@@ -755,24 +790,32 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
             });
 
             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]\\);` +
-                    '};.*' +
-                    `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\\(` +
                                 // order of arguments depends on the parent wrapper parameters order
                                 `[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;
                 });
 
-                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', () => {
@@ -811,15 +862,20 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
             });
 
             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]\\);` +
-                        '};' +
-                        `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;
                 });
 
-                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', () => {
@@ -1026,15 +1086,17 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     const stringArrayWrapperArgumentsRegExpString: string = Array(5)
                         .fill(`-? *${hexadecimalIndexMatch}`)
                         .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]\\);` +
-                        '};.*' +
-                        `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\\(` +
                                     // order of arguments depends on the parent wrapper parameters order
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
@@ -1043,10 +1105,18 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                                     `[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;
                     });
 
-                    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', () => {
@@ -1089,15 +1167,17 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                     const stringArrayWrapperArgumentsRegExpString: string = Array(5)
                         .fill(`(?:-? *${hexadecimalIndexMatch}|(?:'.{4}'))`)
                         .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]\\);` +
-                        '};.*' +
-                        `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\\(` +
                                     // order of arguments depends on the parent wrapper parameters order
                                     `[cdehi](?: *-(?: -)?${hexadecimalIndexMatch})?, *` +
@@ -1106,10 +1186,18 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
                                     `[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;
                     });
 
-                    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', () => {
@@ -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);
                 });
             });
+
+            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', () => {

+ 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/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';
 
 /**

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

@@ -56,6 +56,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
             before(() => {
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArray: true,
                     stringArrayThreshold: 1
                 });
 
@@ -74,11 +75,11 @@ describe('StringArrayStorageAnalyzer', () => {
                 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);
             });
 
-            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);
             });
         });
@@ -101,6 +102,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
             before(() => {
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArray: true,
                     stringArrayThreshold: 1
                 });
 
@@ -118,7 +120,7 @@ describe('StringArrayStorageAnalyzer', () => {
                 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);
             });
         });
@@ -141,6 +143,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
             before(() => {
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArray: true,
                     stringArrayThreshold: 1
                 });
 
@@ -158,7 +161,7 @@ describe('StringArrayStorageAnalyzer', () => {
                 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);
             });
         });
@@ -181,6 +184,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
             before(() => {
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArray: true,
                     stringArrayThreshold: 1
                 });
 
@@ -208,7 +212,7 @@ describe('StringArrayStorageAnalyzer', () => {
                 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);
             });
         });
@@ -232,6 +236,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
             before(() => {
                 stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArray: true,
                     stringArrayThreshold: 1
                 });
 
@@ -249,7 +254,7 @@ describe('StringArrayStorageAnalyzer', () => {
                 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);
             });
         });
@@ -274,6 +279,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
                 before(() => {
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                        stringArray: true,
                         stringArrayThreshold: 0
                     });
 
@@ -291,7 +297,7 @@ describe('StringArrayStorageAnalyzer', () => {
                     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);
                 });
             });
@@ -315,6 +321,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
                 before(() => {
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                        stringArray: true,
                         stringArrayThreshold: 1
                     });
 
@@ -332,7 +339,7 @@ describe('StringArrayStorageAnalyzer', () => {
                     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);
                 });
             });
@@ -375,7 +382,7 @@ describe('StringArrayStorageAnalyzer', () => {
                 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);
             });
         });
@@ -393,6 +400,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
                 before(() => {
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                        stringArray: true,
                         stringArrayThreshold: 0
                     });
 
@@ -410,7 +418,7 @@ describe('StringArrayStorageAnalyzer', () => {
                     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);
                 });
             });
@@ -433,6 +441,7 @@ describe('StringArrayStorageAnalyzer', () => {
 
                 before(() => {
                     stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                        stringArray: true,
                         stringArrayThreshold: 0.5,
                         seed: 3
                     });

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

@@ -307,202 +307,316 @@ describe('obfuscatedCodeFileUtils', () => {
 
     describe('getOutputSourceMapPath', () => {
         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(() => {
                     const obfuscatedCodeFileUtils: ObfuscatedCodeFileUtils = new ObfuscatedCodeFileUtils(
@@ -511,20 +625,20 @@ describe('obfuscatedCodeFileUtils', () => {
                             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;
 
@@ -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;
 
@@ -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 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;
 
@@ -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 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 { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade';
+import { atob } from '../../helpers/atob';
 
 describe('CryptUtils', () => {
     let cryptUtils: ICryptUtils;
@@ -21,30 +22,65 @@ describe('CryptUtils', () => {
 
     describe('btoa', () => {
        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(() => {
-               string = cryptUtils.btoa('string');
+               encodedString = cryptUtils.btoa('string');
+               decodedString = atob(encodedString);
            });
 
            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', () => {
-            const expectedString: string = 'c3RyaQ==';
+            const expectedEncodedString: string = 'c3RyaQ==';
+            const expectedDecodedString: string = 'stri';
 
-            let string: string;
+            let encodedString: string,
+                decodedString: string;
 
             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', () => {
-                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"
   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:
-    comment-parser "^1.1.5"
+    comment-parser "1.1.6-beta.0"
     esquery "^1.4.0"
     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:
     ajv "^6.12.4"
     debug "^4.1.1"
@@ -574,27 +574,35 @@
   resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1"
   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":
   version "1.1.1"
   resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz"
   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"
   resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz"
   integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==
@@ -610,22 +618,17 @@
     "@types/estree" "*"
     "@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:
     "@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@*":
   version "3.0.0"
@@ -666,15 +669,20 @@
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
   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":
   version "0.0.29"
   resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
   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:
     "@types/node" "*"
 
@@ -683,17 +691,17 @@
   resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz"
   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:
     "@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]":
   version "4.0.0"
@@ -707,10 +715,10 @@
   resolved "https://registry.npmjs.org/@types/node/-/node-13.9.3.tgz"
   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":
   version "2.4.0"
@@ -722,10 +730,10 @@
   resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz"
   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:
     "@types/glob" "*"
     "@types/node" "*"
@@ -752,73 +760,73 @@
   resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.16.2.tgz#8db514b059c1b2ae14ce9d7bb325296de6a9a0fa"
   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:
-    "@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"
     functional-red-black-tree "^1.0.1"
     regexpp "^3.1.0"
     semver "^7.3.5"
     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:
     "@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-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:
-    "@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"
 
-"@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:
-    "@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:
-    "@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"
     globby "^11.0.3"
     is-glob "^4.0.1"
     semver "^7.3.5"
     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:
-    "@typescript-eslint/types" "4.28.1"
+    "@typescript-eslint/types" "4.28.4"
     eslint-visitor-keys "^2.0.0"
 
 "@ungap/[email protected]":
@@ -826,125 +834,125 @@
   resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz"
   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:
-    "@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:
-    "@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"
 
-"@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:
-    "@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:
     "@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:
     "@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:
-    "@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:
-    "@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:
-    "@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:
-    "@webassemblyjs/ast" "1.11.0"
+    "@webassemblyjs/ast" "1.11.1"
     "@xtuc/long" "4.2.2"
 
 "@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"
   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:
   version "1.0.2"
   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"
   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:
   version "1.0.1"
@@ -1643,6 +1656,13 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
   dependencies:
     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:
   version "1.2.0"
   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"
   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:
   version "3.0.1"
   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"
     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:
   version "1.2.1"
@@ -1902,14 +1927,14 @@ [email protected]:
     resolve "^1.20.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:
-    "@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"
     jsdoc-type-pratt-parser "^1.0.4"
     lodash "^4.17.21"
@@ -1953,9 +1978,18 @@ [email protected]:
     esrecurse "^4.1.0"
     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"
-  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:
     esrecurse "^4.3.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"
   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:
     "@babel/code-frame" "7.12.11"
-    "@eslint/eslintrc" "^0.4.2"
+    "@eslint/eslintrc" "^0.4.3"
     "@humanwhocodes/config-array" "^0.5.0"
     ajv "^6.10.0"
     chalk "^4.0.0"
@@ -3941,6 +3975,15 @@ schema-utils@^3.0.0:
     ajv "^6.12.5"
     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:
   version "5.7.1"
   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"
   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:
   version "0.5.19"
   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"
     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:
     "@tsconfig/node10" "^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"
   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:
     source-list-map "^2.0.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:
     "@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"
     browserslist "^4.14.5"
     chrome-trace-event "^1.0.2"
     enhanced-resolve "^5.8.0"
-    es-module-lexer "^0.6.0"
+    es-module-lexer "^0.7.1"
     eslint-scope "5.1.1"
     events "^3.2.0"
     glob-to-regexp "^0.4.1"
@@ -4634,11 +4685,11 @@ [email protected]:
     loader-runner "^4.2.0"
     mime-types "^2.1.27"
     neo-async "^2.6.2"
-    schema-utils "^3.0.0"
+    schema-utils "^3.1.0"
     tapable "^2.1.1"
     terser-webpack-plugin "^5.1.3"
     watchpack "^2.2.0"
-    webpack-sources "^2.3.0"
+    webpack-sources "^2.3.1"
 
 which-boxed-primitive@^1.0.2:
   version "1.0.2"

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio