Quellcode durchsuchen

Enabled windows builds (#772)

* Improved hierarchy of generated directories when `--output` is a directory path
* Fixed wrong obfuscated files path generation under `windows`. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/576
* Fixed wrong source map path generation under `windows`. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/760
* `javascript-obfuscator` now can be built under `windows` environment
Timofey Kachalov vor 4 Jahren
Ursprung
Commit
ac671daa94

+ 1 - 1
.eslintrc.js

@@ -205,7 +205,7 @@ module.exports = {
         "import/order": "off",
         "indent": "off",
         "jsdoc/no-types": "off",
-        "linebreak-style": "error",
+        "linebreak-style": "off",
         "max-classes-per-file": [
             "error",
             1

+ 16 - 8
.travis.yml

@@ -1,11 +1,20 @@
 language: node_js
 
-node_js:
-  - "10"
-  - "12"
-  - "13"
-  - "14"
-  - "stable"
+env:
+  - YARN_GPG=no
+
+jobs:
+  include:
+    - os: linux
+      node_js: "10"
+    - os: linux
+      node_js: "12"
+    - os: linux
+      node_js: "13"
+    - os: linux
+      node_js: "stable"
+    - os: windows
+      node_js: "14"
 
 cache:
   yarn: true
@@ -15,5 +24,4 @@ cache:
 script: "yarn run travis"
 
 after_success:
-  - yarn run test:coveralls
-  - rm -rf ./coverage
+  - yarn run test:mocha-coverage:report

+ 7 - 0
CHANGELOG.md

@@ -1,5 +1,12 @@
 Change Log
 
+v2.5.0
+---
+* Improved hierarchy of generated directories when `--output` is a directory path
+* Fixed wrong obfuscated files path generation under `windows`. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/576
+* Fixed wrong source map path generation under `windows`. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/760
+* `javascript-obfuscator` now can be built under `windows` environment
+
 v2.4.3
 ---
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/769

+ 1 - 1
README.md

@@ -35,7 +35,7 @@ The example of obfuscated code: [github.com](https://github.com/javascript-obfus
 [![npm version](https://badge.fury.io/js/javascript-obfuscator.svg)](https://badge.fury.io/js/javascript-obfuscator)
 [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjavascript-obfuscator%2Fjavascript-obfuscator.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjavascript-obfuscator%2Fjavascript-obfuscator?ref=badge_shield)
 [![Build Status](https://travis-ci.com/javascript-obfuscator/javascript-obfuscator.svg?branch=master)](https://travis-ci.com/javascript-obfuscator/javascript-obfuscator)
-[![Coverage Status](https://coveralls.io/repos/github/javascript-obfuscator/javascript-obfuscator/badge.svg?branch=master)](https://coveralls.io/github/javascript-obfuscator/javascript-obfuscator?branch=master)
+[![Coverage Status](https://coveralls.io/repos/github/javascript-obfuscator/javascript-obfuscator/badge.svg)](https://coveralls.io/github/javascript-obfuscator/javascript-obfuscator)
 [![Backers on Open Collective](https://opencollective.com/javascript-obfuscator/backers/badge.svg)](#backers) 
 [![Sponsors on Open Collective](https://opencollective.com/javascript-obfuscator/sponsors/badge.svg)](#sponsors)
 [![xscode](https://img.shields.io/badge/Available%20on-xs%3Acode-blue?style=?style=plastic&logo=appveyor&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF////////VXz1bAAAAAJ0Uk5T/wDltzBKAAAAlUlEQVR42uzXSwqAMAwE0Mn9L+3Ggtgkk35QwcnSJo9S+yGwM9DCooCbgn4YrJ4CIPUcQF7/XSBbx2TEz4sAZ2q1RAECBAiYBlCtvwN+KiYAlG7UDGj59MViT9hOwEqAhYCtAsUZvL6I6W8c2wcbd+LIWSCHSTeSAAECngN4xxIDSK9f4B9t377Wd7H5Nt7/Xz8eAgwAvesLRjYYPuUAAAAASUVORK5CYII=)](https://xscode.com/sanex3339/javascript-obfuscator)

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
dist/index.browser.js


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
dist/index.cli.js


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
dist/index.js


+ 24 - 24
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "2.4.3",
+  "version": "2.5.0",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",
@@ -42,11 +42,11 @@
     "source-map-support": "0.5.19",
     "string-template": "1.0.0",
     "stringz": "2.1.0",
-    "tslib": "2.0.1"
+    "tslib": "2.0.2"
   },
   "devDependencies": {
     "@istanbuljs/nyc-config-typescript": "1.0.1",
-    "@types/chai": "4.2.12",
+    "@types/chai": "4.2.13",
     "@types/chance": "1.1.0",
     "@types/escodegen": "0.0.6",
     "@types/eslint-scope": "3.7.0",
@@ -57,16 +57,17 @@
     "@types/mkdirp": "1.0.1",
     "@types/mocha": "8.0.3",
     "@types/multimatch": "4.0.0",
-    "@types/node": "14.11.2",
+    "@types/node": "14.11.5",
     "@types/rimraf": "3.0.0",
     "@types/sinon": "9.0.8",
     "@types/string-template": "1.0.2",
     "@types/webpack-env": "1.15.3",
-    "@typescript-eslint/eslint-plugin": "4.3.0",
-    "@typescript-eslint/parser": "4.3.0",
+    "@typescript-eslint/eslint-plugin": "4.4.0",
+    "@typescript-eslint/parser": "4.4.0",
     "chai": "4.2.0",
     "chai-exclude": "2.0.2",
     "coveralls": "3.1.0",
+    "cross-env": "7.0.2",
     "eslint": "7.10.0",
     "eslint-plugin-import": "2.22.1",
     "eslint-plugin-jsdoc": "30.6.3",
@@ -80,7 +81,7 @@
     "pjson": "1.0.9",
     "pre-commit": "1.2.2",
     "rimraf": "3.0.2",
-    "sinon": "9.1.0",
+    "sinon": "9.2.0",
     "threads": "1.6.3",
     "ts-loader": "8.0.4",
     "ts-node": "9.0.0",
@@ -95,23 +96,22 @@
   },
   "homepage": "https://obfuscator.io/",
   "scripts": {
-    "start": "scripts/start",
-    "webpack:prod": "scripts/webpack-prod",
-    "webpack:dev": "scripts/webpack-dev",
-    "build": "scripts/build",
-    "watch": "scripts/watch",
-    "test:dev": "scripts/test-dev",
-    "test:devCompilePerformance": "scripts/test-dev-compile-performance",
-    "test:devRuntimePerformance": "scripts/test-dev-runtime-performance",
-    "test:full": "scripts/test-full",
-    "test:coveralls": "scripts/test-coveralls",
-    "test:mocha": "scripts/test-mocha",
-    "test:mocha-coverage": "scripts/test-mocha-coverage",
-    "test:mocha-memory-performance": "scripts/test-mocha-memory-performance",
-    "test": "scripts/test",
-    "eslint": "scripts/eslint",
-    "travis": "scripts/travis",
-    "git:addFiles": "scripts/git-add-files",
+    "start": "yarn run watch",
+    "webpack:prod": "webpack --config webpack/webpack.node.config.js --config webpack/webpack.browser.config.js --mode production",
+    "build": "yarn run webpack:prod && yarn run eslint && yarn test",
+    "watch": "webpack --config webpack/webpack.node.config.js --mode development --watch",
+    "test:dev": "ts-node --type-check test/dev/dev.ts",
+    "test:devCompilePerformance": "ts-node test/dev/dev-compile-performance.ts",
+    "test:devRuntimePerformance": "ts-node test/dev/dev-runtime-performance.ts",
+    "test:full": "yarn run test:dev && yarn run test:mocha-coverage && yarn run test:mocha-memory-performance",
+    "test:mocha": "mocha --require ts-node/register --require source-map-support/register test/index.spec.ts --exit",
+    "test:mocha-coverage": "nyc --reporter text-summary --no-clean yarn run test:mocha",
+    "test:mocha-coverage:report": "nyc report --reporter=text-lcov | coveralls",
+    "test:mocha-memory-performance": "cross-env NODE_OPTIONS=--max-old-space-size=220 mocha --require ts-node/register test/performance-tests/JavaScriptObfuscatorMemory.spec.ts",
+    "test": "yarn run test:full",
+    "eslint": "eslint src/**/*.ts",
+    "travis": "yarn run eslint && yarn run test",
+    "git:addFiles": "git add .",
     "postinstall": "opencollective || exit 0"
   },
   "pre-commit": [

+ 0 - 5
scripts/build

@@ -1,5 +0,0 @@
-#!/bin/bash
-
-yarn run webpack:prod &&
-yarn run eslint &&
-yarn test

+ 0 - 3
scripts/eslint

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-$(yarn bin)/eslint src/**/*.ts

+ 0 - 3
scripts/git-add-files

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-git add .

+ 0 - 3
scripts/start

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-yarn run watch

+ 0 - 3
scripts/test

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-yarn run test:full

+ 0 - 4
scripts/test-coveralls

@@ -1,4 +0,0 @@
-#!/bin/bash
-
-yarn run test:mocha &&
-$(yarn bin)/nyc report --reporter=text-lcov | $(yarn bin)/coveralls

+ 0 - 3
scripts/test-dev

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-$(yarn bin)/ts-node --type-check test/dev/dev.ts

+ 0 - 3
scripts/test-dev-compile-performance

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-$(yarn bin)/ts-node test/dev/dev-compile-performance.ts

+ 0 - 3
scripts/test-dev-runtime-performance

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-$(yarn bin)/ts-node test/dev/dev-runtime-performance.ts

+ 0 - 5
scripts/test-full

@@ -1,5 +0,0 @@
-#!/bin/bash
-
-yarn run test:dev &&
-yarn run test:mocha-coverage &&
-yarn run test:mocha-memory-performance

+ 0 - 3
scripts/test-mocha

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-$(yarn bin)/mocha --require ts-node/register --require source-map-support/register test/index.spec.ts --exit

+ 0 - 3
scripts/test-mocha-coverage

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-$(yarn bin)/nyc --reporter text-summary yarn run test:mocha

+ 0 - 3
scripts/test-mocha-memory-performance

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-NODE_OPTIONS=--max-old-space-size=220 $(yarn bin)/mocha --require ts-node/register test/performance-tests/JavaScriptObfuscatorMemory.spec.ts

+ 0 - 4
scripts/travis

@@ -1,4 +0,0 @@
-#!/bin/bash
-
-yarn run eslint &&
-yarn test

+ 0 - 3
scripts/watch

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-$(yarn bin)/webpack --config webpack/webpack.node.config.js --mode development --watch

+ 0 - 3
scripts/webpack-dev

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-$(yarn bin)/webpack --config webpack/webpack.node.config.js --mode production

+ 0 - 3
scripts/webpack-prod

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-$(yarn bin)/webpack --config webpack/webpack.node.config.js --config webpack/webpack.browser.config.js --mode production

+ 33 - 12
src/cli/utils/ObfuscatedCodeWriter.ts

@@ -27,7 +27,7 @@ export class ObfuscatedCodeWriter {
         inputPath: string,
         options: TInputCLIOptions
     ) {
-        this.inputPath = inputPath;
+        this.inputPath = path.normalize(inputPath);
         this.options = options;
     }
 
@@ -36,13 +36,13 @@ export class ObfuscatedCodeWriter {
      * @returns {string}
      */
     public getOutputCodePath (filePath: string): string {
+        const normalizedFilePath: string = path.normalize(filePath);
         const normalizedRawOutputPath: string | null = this.options.output
             ? path.normalize(this.options.output)
             : null;
 
         if (!normalizedRawOutputPath) {
-            return path
-                .normalize(filePath)
+            return normalizedFilePath
                 .split(StringSeparator.Dot)
                 .map((value: string, index: number) => {
                     return index === 0 ? `${value}${JavaScriptObfuscatorCLI.obfuscatedFilePrefix}` : value;
@@ -60,7 +60,9 @@ export class ObfuscatedCodeWriter {
 
         if (isDirectoryRawInputPath) {
             if (isDirectoryRawOutputPath) {
-                return path.join(normalizedRawOutputPath, filePath);
+                const baseOutputPath: string = normalizedFilePath.replace(this.inputPath, '');
+
+                return path.join(normalizedRawOutputPath, baseOutputPath);
             } else {
                 throw new Error('Output path for directory obfuscation should be a directory path');
             }
@@ -79,19 +81,38 @@ export class ObfuscatedCodeWriter {
      * @returns {string}
      */
     public getOutputSourceMapPath (outputCodePath: string, sourceMapFileName: string = ''): string {
+        if (!outputCodePath) {
+            throw new Error('Output code path is empty');
+        }
+
+        let normalizedOutputCodePath: string = path.normalize(outputCodePath);
+        let parsedOutputCodePath: path.ParsedPath = path.parse(normalizedOutputCodePath);
+
+        if (!parsedOutputCodePath.ext && !sourceMapFileName) {
+            throw new Error('Source map file name should be set when output code path is a directory path');
+        }
+
         if (sourceMapFileName) {
-            outputCodePath = `${outputCodePath.substring(
-                0, outputCodePath.lastIndexOf('/')
-            )}/${sourceMapFileName}`;
+            const indexOfLastSeparator: number = normalizedOutputCodePath.lastIndexOf(path.sep);
+            const sourceMapPath: string = parsedOutputCodePath.ext && indexOfLastSeparator > 0
+                ? normalizedOutputCodePath.slice(0, indexOfLastSeparator)
+                : normalizedOutputCodePath;
+            // remove possible drive letter for win32 environment
+            const normalizedSourceMapFilePath: string = sourceMapFileName.replace(/^[a-zA-Z]:\\*/, '');
+
+            normalizedOutputCodePath = path.join(sourceMapPath, normalizedSourceMapFilePath);
         }
 
-        if (!/\.js\.map$/.test(outputCodePath)) {
-            outputCodePath = `${outputCodePath.split(StringSeparator.Dot)[0]}.js.map`;
-        } else if (/\.js$/.test(outputCodePath)) {
-            outputCodePath += '.map';
+        if (!/\.js\.map$/.test(normalizedOutputCodePath)) {
+            parsedOutputCodePath = path.parse(normalizedOutputCodePath);
+            const outputCodePathWithoutExtension: string = path.join(parsedOutputCodePath.dir, parsedOutputCodePath.name);
+
+            normalizedOutputCodePath = `${outputCodePathWithoutExtension}.js.map`;
+        } else if (/\.js$/.test(normalizedOutputCodePath)) {
+            normalizedOutputCodePath += '.map';
         }
 
-        return outputCodePath;
+        return normalizedOutputCodePath;
     }
 
     /**

+ 1 - 1
src/cli/utils/SourceCodeReader.ts

@@ -139,7 +139,7 @@ export class SourceCodeReader {
     private readDirectoryRecursive (directoryPath: string, filesData: IFileData[] = []): IFileData[] {
         fs.readdirSync(directoryPath, JavaScriptObfuscatorCLI.encoding)
             .forEach((fileName: string) => {
-                const filePath: string = `${directoryPath}/${fileName}`;
+                const filePath: string = path.join(directoryPath, fileName);
 
                 if (
                     SourceCodeReader.isDirectoryPath(filePath)

+ 47 - 47
test/functional-tests/cli/JavaScriptObfuscatorCLI.spec.ts

@@ -15,15 +15,15 @@ describe('JavaScriptObfuscatorCLI', function (): void {
 
     const expectedError: RegExp = /Given input path must be a valid/;
 
-    const fixturesDirName: string = 'test/fixtures';
+    const fixturesDirName: string = path.join('test', 'fixtures');
     const fixtureFileName: string = 'sample.js';
-    const fixtureFilePath: string = `${fixturesDirName}/${fixtureFileName}`;
-    const outputDirName: string = 'test/tmp';
+    const fixtureFilePath: string = path.join(fixturesDirName, fixtureFileName);
+    const outputDirName: string = path.join('test', 'tmp');
     const outputFileName: string = 'sample-obfuscated.js';
-    const outputFilePath: string = `${outputDirName}/${outputFileName}`;
-    const configDirName: string = 'test/fixtures';
+    const outputFilePath: string = path.join(outputDirName, outputFileName);
+    const configDirName: string = path.join('test', 'fixtures');
     const configFileName: string = 'config.js';
-    const configFilePath: string = `${configDirName}/${configFileName}`;
+    const configFilePath: string = path.join(configDirName, configFileName);
 
 
     describe('run', () => {
@@ -66,7 +66,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         isFileExist: boolean;
 
                     before(() => {
-                        outputFixturesFilePath = `${fixturesDirName}/${outputFileName}`;
+                        outputFixturesFilePath = path.join(fixturesDirName, outputFileName);
 
                         JavaScriptObfuscatorCLI.obfuscate([
                             'node',
@@ -93,7 +93,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         testFunc = () => JavaScriptObfuscatorCLI.obfuscate([
                             'node',
                             'javascript-obfuscator',
-                            'wrong/file/path'
+                            path.join('wrong', 'file', 'path')
                         ]);
                     });
 
@@ -105,7 +105,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                 describe('Variant #3: input file extension isn\'t `.js`', () => {
                     const expectedError: RegExp = /Given input path must be a valid/;
                     const outputFileName: string = 'sample-obfuscated.ts';
-                    const outputFilePath: string = `${outputDirName}/${outputFileName}`;
+                    const outputFilePath: string = path.join(outputDirName, outputFileName);
 
                     let testFunc: () => void;
 
@@ -141,7 +141,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                             '--output',
                             outputFilePath,
                             '--exclude',
-                            '**/foo.js'
+                            path.join('**', 'foo.js')
                         ]);
 
                         isFileExist = fs.existsSync(outputFilePath);
@@ -163,7 +163,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                             '--output',
                             outputFilePath,
                             '--exclude',
-                            '**/sample.js'
+                            path.join('**', 'sample.js')
                         ]);
                     });
 
@@ -180,7 +180,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
 
         describe('Variant #2: obfuscation of directory', () => {
             describe(`Variant #1: default behaviour`, () => {
-                const directoryPath: string = `${fixturesDirName}/directory-obfuscation`;
+                const directoryPath: string = path.join(fixturesDirName, 'directory-obfuscation');
                 const outputFileName1: string = 'foo-obfuscated.js';
                 const outputFileName2: string = 'bar-obfuscated.js';
                 const outputFileName3: string = 'baz-obfuscated.js';
@@ -198,9 +198,9 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                     fileContent2: string;
 
                 before(() => {
-                    outputFixturesFilePath1 = `${directoryPath}/${outputFileName1}`;
-                    outputFixturesFilePath2 = `${directoryPath}/${outputFileName2}`;
-                    outputFixturesFilePath3 = `${directoryPath}/${outputFileName3}`;
+                    outputFixturesFilePath1 = path.join(directoryPath, outputFileName1);
+                    outputFixturesFilePath2 = path.join(directoryPath, outputFileName2);
+                    outputFixturesFilePath3 = path.join(directoryPath, outputFileName3);
 
                     JavaScriptObfuscatorCLI.obfuscate([
                         'node',
@@ -245,7 +245,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
             });
 
             describe('Variant #2: obfuscation of directory with `identifiersPrefix` option value', () => {
-                const directoryPath: string = `${fixturesDirName}/directory-obfuscation`;
+                const directoryPath: string = path.join(fixturesDirName, 'directory-obfuscation');
                 const identifiersPrefix: string = 'foo';
                 const outputFileName1: string = 'foo-obfuscated.js';
                 const outputFileName2: string = 'bar-obfuscated.js';
@@ -261,8 +261,8 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                     fileContent2: string;
 
                 before(() => {
-                    outputFixturesFilePath1 = `${directoryPath}/${outputFileName1}`;
-                    outputFixturesFilePath2 = `${directoryPath}/${outputFileName2}`;
+                    outputFixturesFilePath1 = path.join(directoryPath, outputFileName1);
+                    outputFixturesFilePath2 = path.join(directoryPath, outputFileName2);
 
                     JavaScriptObfuscatorCLI.obfuscate([
                         'node',
@@ -304,9 +304,9 @@ describe('JavaScriptObfuscatorCLI', function (): void {
             });
 
             describe('Variant #3: obfuscation of directory with `output` option', () => {
-                const directoryPath: string = `${fixturesDirName}/directory-obfuscation`;
+                const directoryPath: string = path.join(fixturesDirName, 'directory-obfuscation');
                 const outputDirectoryName: string = 'obfuscated';
-                const outputDirectoryPath: string = `${directoryPath}/${outputDirectoryName}`;
+                const outputDirectoryPath: string = path.join(directoryPath, outputDirectoryName);
                 const outputFileName1: string = 'foo.js';
                 const outputFileName2: string = 'bar.js';
                 const outputFileName3: string = 'baz.js';
@@ -319,9 +319,9 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                     isFileExist3: boolean;
 
                 before(() => {
-                    outputFixturesFilePath1 = `${outputDirectoryPath}/${directoryPath}/${outputFileName1}`;
-                    outputFixturesFilePath2 = `${outputDirectoryPath}/${directoryPath}/${outputFileName2}`;
-                    outputFixturesFilePath3 = `${outputDirectoryPath}/${directoryPath}/${outputFileName3}`;
+                    outputFixturesFilePath1 = path.join(outputDirectoryPath, outputFileName1);
+                    outputFixturesFilePath2 = path.join(outputDirectoryPath, outputFileName2);
+                    outputFixturesFilePath3 = path.join(outputDirectoryPath, outputFileName3);
 
                     JavaScriptObfuscatorCLI.obfuscate([
                         'node',
@@ -338,7 +338,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
 
                 it(
                     `should create file \`${outputFileName1}\` with obfuscated code in ` +
-                    `\`${fixturesDirName}/${outputDirectoryName}\` directory`,
+                    `\`${path.join(fixturesDirName, outputDirectoryName)}\` directory`,
                     () => {
                         assert.equal(isFileExist1, true);
                     }
@@ -346,7 +346,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
 
                 it(
                     `should create file \`${outputFileName2}\` with obfuscated code in ` +
-                    `\`${fixturesDirName}/${outputDirectoryName}\` directory`,
+                    `\`${path.join(fixturesDirName, outputDirectoryName)}\` directory`,
                     () => {
                         assert.equal(isFileExist2, true);
                     }
@@ -354,7 +354,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
 
                 it(
                     `shouldn't create file \`${outputFileName3}\` in ` +
-                    `\`${fixturesDirName}/${outputDirectoryName}\` directory`,
+                    `\`${path.join(fixturesDirName, outputDirectoryName)}\` directory`,
                     () => {
                         assert.equal(isFileExist3, false);
                     }
@@ -367,7 +367,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
 
             describe('Variant #4: --exclude option', () => {
                 describe('Variant #1: --exclude option is pointed on different file', () => {
-                    const directoryPath: string = `${fixturesDirName}/directory-obfuscation`;
+                    const directoryPath: string = path.join(fixturesDirName, 'directory-obfuscation');
                     const outputFileName1: string = 'foo-obfuscated.js';
                     const outputFileName2: string = 'bar-obfuscated.js';
                     const outputFileName3: string = 'baz-obfuscated.js';
@@ -385,16 +385,16 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         fileContent2: string;
 
                     before(() => {
-                        outputFixturesFilePath1 = `${directoryPath}/${outputFileName1}`;
-                        outputFixturesFilePath2 = `${directoryPath}/${outputFileName2}`;
-                        outputFixturesFilePath3 = `${directoryPath}/${outputFileName3}`;
+                        outputFixturesFilePath1 = path.join(directoryPath, outputFileName1);
+                        outputFixturesFilePath2 = path.join(directoryPath, outputFileName2);
+                        outputFixturesFilePath3 = path.join(directoryPath, outputFileName3);
 
                         JavaScriptObfuscatorCLI.obfuscate([
                             'node',
                             'javascript-obfuscator',
                             directoryPath,
                             '--exclude',
-                            '**/bark.js',
+                            path.join('**', 'bark.js'),
                             '--rename-globals',
                             'true'
                         ]);
@@ -434,7 +434,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                 });
 
                 describe('Variant #2: --exclude option is pointed on file under obfuscating directory', () => {
-                    const directoryPath: string = `${fixturesDirName}/directory-obfuscation`;
+                    const directoryPath: string = path.join(fixturesDirName, 'directory-obfuscation');
                     const outputFileName1: string = 'foo-obfuscated.js';
                     const outputFileName2: string = 'bar-obfuscated.js';
                     const outputFileName3: string = 'baz-obfuscated.js';
@@ -450,16 +450,16 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                         fileContent1: string;
 
                     before(() => {
-                        outputFixturesFilePath1 = `${directoryPath}/${outputFileName1}`;
-                        outputFixturesFilePath2 = `${directoryPath}/${outputFileName2}`;
-                        outputFixturesFilePath3 = `${directoryPath}/${outputFileName3}`;
+                        outputFixturesFilePath1 = path.join(directoryPath, outputFileName1);
+                        outputFixturesFilePath2 = path.join(directoryPath, outputFileName2);
+                        outputFixturesFilePath3 = path.join(directoryPath, outputFileName3);
 
                         JavaScriptObfuscatorCLI.obfuscate([
                             'node',
                             'javascript-obfuscator',
                             directoryPath,
                             '--exclude',
-                            '**/foo.js',
+                            path.join('**', 'foo.js'),
                             '--rename-globals',
                             'true'
                         ]);
@@ -619,7 +619,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                     const expectedSourceMapSourceName: string = path.basename(fixtureFileName);
                     const sourceMapFileName: string = 'test';
                     const sourceMapFilePath: string = `${sourceMapFileName}.js.map`;
-                    const outputSourceMapFilePath: string = `${outputDirName}/${sourceMapFilePath}`;
+                    const outputSourceMapFilePath: string = path.join(outputDirName, sourceMapFilePath);
 
                     let isFileExist: boolean,
                         sourceMapObject: any;
@@ -864,7 +864,7 @@ describe('JavaScriptObfuscatorCLI', function (): void {
             });
 
             describe('`--exclude` option', () => {
-                const directoryPath: string = `${fixturesDirName}/directory-obfuscation`;
+                const directoryPath: string = path.join(fixturesDirName, 'directory-obfuscation');
                 const outputFileName1: string = 'foo-obfuscated.js';
                 const outputFileName2: string = 'bar-obfuscated.js';
 
@@ -874,8 +874,8 @@ describe('JavaScriptObfuscatorCLI', function (): void {
                     isFileExist2: boolean;
 
                 before(() => {
-                    outputFixturesFilePath1 = `${directoryPath}/${outputFileName1}`;
-                    outputFixturesFilePath2 = `${directoryPath}/${outputFileName2}`;
+                    outputFixturesFilePath1 = path.join(directoryPath, outputFileName1);
+                    outputFixturesFilePath2 = path.join(directoryPath, outputFileName2);
 
                     JavaScriptObfuscatorCLI.obfuscate([
                         'node',
@@ -942,17 +942,17 @@ describe('JavaScriptObfuscatorCLI', function (): void {
 
         describe('Logging', () => {
             describe('Obfuscating file message', () => {
-                const directoryPath: string = `${fixturesDirName}/directory-obfuscation`;
+                const directoryPath: string = path.join(fixturesDirName, 'directory-obfuscation');
 
                 const inputFileName1: string = 'foo.js';
                 const inputFileName2: string = 'bar.js';
-                const inputFilePath1: string = `${directoryPath}/${inputFileName1}`;
-                const inputFilePath2: string = `${directoryPath}/${inputFileName2}`;
+                const inputFilePath1: string = path.join(directoryPath, inputFileName1);
+                const inputFilePath2: string = path.join(directoryPath, inputFileName2);
 
                 const outputFileName1: string = 'foo-obfuscated.js';
                 const outputFileName2: string = 'bar-obfuscated.js';
-                const outputFilePath1: string = `${directoryPath}/${outputFileName1}`;
-                const outputFilePath2: string = `${directoryPath}/${outputFileName2}`;
+                const outputFilePath1: string = path.join(directoryPath, outputFileName1);
+                const outputFilePath2: string = path.join(directoryPath, outputFileName2);
 
                 const expectedLoggingMessage1: string = `[javascript-obfuscator-cli] Obfuscating file: ${inputFilePath1}...`;
                 const expectedLoggingMessage2: string = `[javascript-obfuscator-cli] Obfuscating file: ${inputFilePath2}...`;
@@ -992,10 +992,10 @@ describe('JavaScriptObfuscatorCLI', function (): void {
             });
 
             describe('Error message', () => {
-                const directoryPath: string = `${fixturesDirName}/directory-obfuscation-error`;
+                const directoryPath: string = path.join(fixturesDirName, 'directory-obfuscation-error');
 
                 const inputFileName: string = 'foo.js';
-                const inputFilePath: string = `${directoryPath}/${inputFileName}`;
+                const inputFilePath: string = path.join(directoryPath, inputFileName);
 
                 const expectedLoggingMessage1: string = `[javascript-obfuscator-cli] Error in file: ${inputFilePath}...`;
 

+ 5 - 3
test/functional-tests/code-transformers/preparing-transformers/hashbang-operator-transformer/HashbangOperatorTransformer.spec.ts

@@ -7,9 +7,11 @@ import { readFileAsString } from '../../../../helpers/readFileAsString';
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
 
 describe('HashbangOperatorTransformer', () => {
+    const lineSeparator: string = '\r?\n';
+
     describe('Variant #1: simple', () => {
         const regExp: RegExp = new RegExp(
-            `^#!\/usr\/bin\/env node\n` +
+            `^#!\/usr\/bin\/env node${lineSeparator}` +
             `var foo *= *'abc';`
         );
 
@@ -33,7 +35,7 @@ describe('HashbangOperatorTransformer', () => {
 
     describe('Variant #2: multiple new lines', () => {
         const regExp: RegExp = new RegExp(
-            `^#!\/usr\/bin\/env node\n\n\n\n` +
+            `^#!\/usr\/bin\/env node${lineSeparator.repeat(4)}` +
             `var foo *= *'abc';`
         );
 
@@ -57,7 +59,7 @@ describe('HashbangOperatorTransformer', () => {
 
     describe('Variant #3: `stringArray` option enabled', () => {
         const regExp: RegExp = new RegExp(
-            `^#!\/usr\/bin\/env node\n` +
+            `^#!\/usr\/bin\/env node${lineSeparator}` +
             `var _0x(\\w){4} *= *\\['abc'];`
         );
 

+ 8 - 6
test/functional-tests/node-transformers/initializing-transformers/comments-transformer/CommentsTransformer.spec.ts

@@ -7,6 +7,8 @@ import { readFileAsString } from '../../../../helpers/readFileAsString';
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
 
 describe('CommentsTransformer', () => {
+    const lineSeparatorEscaped: string = '\\r?\\n';
+
     describe('Variant #1: simple comment without preserved words', () => {
         const regExp: RegExp = /^var test *= *0x1;$/;
 
@@ -72,10 +74,10 @@ describe('CommentsTransformer', () => {
 
     describe('Variant #4: comment with preserved and non-preserved words', () => {
         const regExp: RegExp = new RegExp(``+
-            `^\\/\\*\\* *\\n` +
-            ` *\\* *@license *\\n` +
-            ` *\\* *test\\n` +
-            ` *\\*\\/\\n` +
+            `^\\/\\*\\* *${lineSeparatorEscaped}` +
+            ` *\\* *@license *${lineSeparatorEscaped}` +
+            ` *\\* *test${lineSeparatorEscaped}` +
+            ` *\\*\\/${lineSeparatorEscaped}` +
             `var test *= *0x1;` +
             ` *\\/\\*\\* *@preserved *\\*\\/$` +
         ``);
@@ -117,7 +119,7 @@ describe('CommentsTransformer', () => {
         });
     });
 
-    describe('Variant #5: only comment with preserved words', () => {
+    describe('Variant #6: only comment with preserved words', () => {
         const regExp: RegExp = /^\/\/ *@license$/;
 
         let obfuscatedCode: string;
@@ -138,7 +140,7 @@ describe('CommentsTransformer', () => {
         });
     });
 
-    describe('Variant #6: simple comment with preserved words and additional code helper is inserted', () => {
+    describe('Variant #7: simple comment with preserved words and additional code helper is inserted', () => {
         describe('Variant #1: `stringArray` code helper', () => {
             const regExp: RegExp = /^\/\/ *@license *test *comment *\n*var _0x([a-f0-9]){4} *= *\['abc'];/;
 

+ 382 - 37
test/unit-tests/cli/utils/ObfuscatedCodeWriter.spec.ts

@@ -1,7 +1,8 @@
 import { assert } from 'chai';
+import * as fs from 'fs';
 import * as mkdirp from 'mkdirp';
+import * as path from 'path';
 import * as rimraf from 'rimraf';
-import * as fs from "fs";
 
 import { ObfuscatedCodeWriter } from '../../../../src/cli/utils/ObfuscatedCodeWriter';
 
@@ -10,18 +11,18 @@ describe('ObfuscatedCodeWriter', () => {
 
     describe('getOutputCodePath', () => {
         before(() => {
-            mkdirp.sync(`${tmpDirectoryPath}/input/`);
+            mkdirp.sync(path.join(tmpDirectoryPath, 'input', 'nested',));
             fs.writeFileSync(
-                `${tmpDirectoryPath}/input/test-input.js`,
+                path.join(tmpDirectoryPath, 'input', 'nested', 'test-input.js'),
                 'var foo = 1;'
             );
         });
 
         describe('Variant #1: raw input path is a file path, raw output path is a file path', () => {
-            const inputPath: string = `${tmpDirectoryPath}/input/test-input.js`;
-            const rawInputPath: string = `${tmpDirectoryPath}/input/test-input.js`;
-            const rawOutputPath: string = `${tmpDirectoryPath}/output/test-output.js`;
-            const expectedOutputCodePath: string = `${tmpDirectoryPath}/output/test-output.js`;
+            const inputPath: string = path.join(tmpDirectoryPath, 'input', 'nested', 'test-input.js');
+            const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'nested', 'test-input.js');
+            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+            const expectedOutputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
 
             let outputCodePath: string;
 
@@ -41,10 +42,10 @@ describe('ObfuscatedCodeWriter', () => {
         });
 
         describe('Variant #2: raw input path is a file path, raw output path is a directory path', () => {
-            const inputPath: string = `${tmpDirectoryPath}/input/test-input.js`;
-            const rawInputPath: string = `${tmpDirectoryPath}/input/test-input.js`;
-            const rawOutputPath: string = `${tmpDirectoryPath}/output`;
-            const expectedOutputCodePath: string = `${tmpDirectoryPath}/output/test-input.js`;
+            const inputPath: string = path.join(tmpDirectoryPath, 'input', 'nested', 'test-input.js');
+            const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'nested', 'test-input.js');
+            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output');
+            const expectedOutputCodePath: string = path.join(tmpDirectoryPath, 'output', 'test-input.js');
 
             let outputCodePath: string;
 
@@ -64,9 +65,9 @@ describe('ObfuscatedCodeWriter', () => {
         });
 
         describe('Variant #3: raw input path is a directory path, raw output path is a file path', () => {
-            const inputPath: string = `${tmpDirectoryPath}/input/test-input.js`;
-            const rawInputPath: string = `${tmpDirectoryPath}/input`;
-            const rawOutputPath: string = `${tmpDirectoryPath}/output/test-output.js`;
+            const inputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
+            const rawInputPath: string = path.join(tmpDirectoryPath, 'input');
+            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
 
             let testFunc: () => string;
 
@@ -87,10 +88,15 @@ describe('ObfuscatedCodeWriter', () => {
 
         describe('Variant #4: raw input path is a directory path, raw output path is a directory path', () => {
             describe('Variant #1: base directory name', () => {
-                const inputPath: string = `${tmpDirectoryPath}/input/test-input.js`;
-                const rawInputPath: string = `${tmpDirectoryPath}/input`;
-                const rawOutputPath: string = `${tmpDirectoryPath}/output`;
-                const expectedOutputCodePath: string = `${tmpDirectoryPath}/output/${tmpDirectoryPath}/input/test-input.js`;
+                const inputPath: string = path.join(tmpDirectoryPath, 'input', 'nested', 'test-input.js');
+                const rawInputPath: string = path.join(tmpDirectoryPath, 'input');
+                const rawOutputPath: string = path.join(tmpDirectoryPath, 'output');
+                const expectedOutputCodePath: string = path.join(
+                    tmpDirectoryPath,
+                    'output',
+                    'nested',
+                    'test-input.js'
+                );
 
                 let outputCodePath: string;
 
@@ -110,10 +116,16 @@ describe('ObfuscatedCodeWriter', () => {
             });
 
             describe('Variant #2: directory name with dot', () => {
-                const inputPath: string = `${tmpDirectoryPath}/input/test-input.js`;
-                const rawInputPath: string = `${tmpDirectoryPath}/input`;
-                const rawOutputPath: string = `${tmpDirectoryPath}/output/foo.bar`;
-                const expectedOutputCodePath: string = `${tmpDirectoryPath}/output/foo.bar/${tmpDirectoryPath}/input/test-input.js`;
+                const inputPath: string = path.join(tmpDirectoryPath, 'input', 'nested', 'test-input.js');
+                const rawInputPath: string = path.join(tmpDirectoryPath, 'input');
+                const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'foo.bar');
+                const expectedOutputCodePath: string = path.join(
+                    tmpDirectoryPath,
+                    'output',
+                    'foo.bar',
+                    'nested',
+                    'test-input.js'
+                );
 
                 let outputCodePath: string;
 
@@ -133,31 +145,364 @@ describe('ObfuscatedCodeWriter', () => {
             });
         });
 
+        describe('Variant #5: Win32 environment', () => {
+            const baseDirnamePath: string = __dirname;
+
+            before(() => {
+                mkdirp.sync(path.join(baseDirnamePath, tmpDirectoryPath, 'input', 'nested'));
+                fs.writeFileSync(
+                    path.join(baseDirnamePath, tmpDirectoryPath, 'input', 'nested', 'test-input.js'),
+                    'var foo = 1;'
+                );
+            });
+
+            describe('Variant #1: raw input absolute path is a directory path, raw output absolute path is a directory path', () => {
+                describe('Variant #1: base directory name', () => {
+                    const inputPath: string = path.join(baseDirnamePath, tmpDirectoryPath, 'input', 'nested', 'test-input.js');
+                    const rawInputPath: string = path.join(baseDirnamePath, tmpDirectoryPath, 'input');
+                    const rawOutputPath: string = path.join(baseDirnamePath, tmpDirectoryPath, 'output');
+                    const expectedOutputCodePath: string = path.join(
+                        baseDirnamePath,
+                        tmpDirectoryPath,
+                        'output',
+                        'nested',
+                        'test-input.js'
+                    );
+
+                    let outputCodePath: string;
+
+                    before(() => {
+                        const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                            rawInputPath,
+                            {
+                                output: rawOutputPath
+                            }
+                        );
+                        outputCodePath = obfuscatedCodeWriter.getOutputCodePath(inputPath);
+                    });
+
+                    it('should return output path that contains raw output path and actual file input path', () => {
+                        assert.equal(outputCodePath, expectedOutputCodePath);
+                    });
+                });
+            });
+
+            after(() => {
+                rimraf.sync(path.join(baseDirnamePath, tmpDirectoryPath));
+            });
+        });
+
         after(() => {
             rimraf.sync(tmpDirectoryPath);
         });
     });
 
     describe('getOutputSourceMapPath', () => {
-        const rawInputPath: string = `${tmpDirectoryPath}/input/test-input.js`;
-        const rawOutputPath: string = `${tmpDirectoryPath}/output/test-output.js`;
-        const outputCodePath: string = `${tmpDirectoryPath}/output/test-output.js`;
-        const expectedOutputSourceMapPath: string = `${tmpDirectoryPath}/output/test-output.js.map`;
+        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;
+            let outputSourceMapPath: string;
 
-        before(() => {
-            const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
-                rawInputPath,
-                {
-                    output: rawOutputPath
-                }
-            );
-            outputSourceMapPath = obfuscatedCodeWriter.getOutputSourceMapPath(outputCodePath);
+            before(() => {
+                const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                    rawInputPath,
+                    {
+                        output: rawOutputPath
+                    }
+                );
+                outputSourceMapPath = obfuscatedCodeWriter.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 obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                    rawInputPath,
+                    {
+                        output: rawOutputPath
+                    }
+                );
+                testFunc = () => obfuscatedCodeWriter.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;
+
+            before(() => {
+                const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                    rawInputPath,
+                    {
+                        output: rawOutputPath
+                    }
+                );
+                outputSourceMapPath = obfuscatedCodeWriter.getOutputSourceMapPath(outputCodePath);
+            });
+
+            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');
+
+            let outputSourceMapPath: string;
+
+            before(() => {
+                const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                    rawInputPath,
+                    {
+                        output: rawOutputPath
+                    }
+                );
+                outputSourceMapPath = obfuscatedCodeWriter.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;
+
+            before(() => {
+                const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                    rawInputPath,
+                    {
+                        output: rawOutputPath
+                    }
+                );
+                outputSourceMapPath = obfuscatedCodeWriter.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+            });
+
+            it('should return output path for source map', () => {
+                assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+            });
+        });
+
+        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;
+
+            before(() => {
+                const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                    rawInputPath,
+                    {
+                        output: rawOutputPath
+                    }
+                );
+                outputSourceMapPath = obfuscatedCodeWriter.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+            });
+
+            it('should return output path for source map', () => {
+                assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+            });
+        });
+
+        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;
+
+            before(() => {
+                const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                    rawInputPath,
+                    {
+                        output: rawOutputPath
+                    }
+                );
+                outputSourceMapPath = obfuscatedCodeWriter.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+            });
+
+            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');
+
+            let outputSourceMapPath: string;
+
+            before(() => {
+                const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                    rawInputPath,
+                    {
+                        output: rawOutputPath
+                    }
+                );
+                outputSourceMapPath = obfuscatedCodeWriter.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+            });
+
+            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');
+
+                let outputSourceMapPath: string;
+
+                before(() => {
+                    const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                        rawInputPath,
+                        {
+                            output: rawOutputPath
+                        }
+                    );
+                    outputSourceMapPath = obfuscatedCodeWriter.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                });
+
+                it('should return output path for source map', () => {
+                    assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                });
+            });
+
+            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');
+
+                let outputSourceMapPath: string;
+
+                before(() => {
+                    const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                        rawInputPath,
+                        {
+                            output: rawOutputPath
+                        }
+                    );
+                    outputSourceMapPath = obfuscatedCodeWriter.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                });
+
+                it('should return output path for source map', () => {
+                    assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                });
+            });
+
+            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');
+
+                let outputSourceMapPath: string;
+
+                before(() => {
+                    const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                        rawInputPath,
+                        {
+                            output: rawOutputPath
+                        }
+                    );
+                    outputSourceMapPath = obfuscatedCodeWriter.getOutputSourceMapPath(outputCodePath, sourceMapFileName);
+                });
+
+                it('should return output path for source map', () => {
+                    assert.equal(outputSourceMapPath, expectedOutputSourceMapPath);
+                });
+            });
+
+            describe('Variant #4: 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;
+
+                before(() => {
+                    const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                        rawInputPath,
+                        {
+                            output: rawOutputPath
+                        }
+                    );
+                    outputSourceMapPath = obfuscatedCodeWriter.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 #10: empty paths', () => {
+            const rawInputPath: string = path.join(tmpDirectoryPath, 'input', 'test-input.js');
+            const rawOutputPath: string = path.join(tmpDirectoryPath, 'output', 'test-output.js');
+
+            let testFunc: () => string;
+
+            before(() => {
+                const obfuscatedCodeWriter: ObfuscatedCodeWriter = new ObfuscatedCodeWriter(
+                    rawInputPath,
+                    {
+                        output: rawOutputPath
+                    }
+                );
+                testFunc = () => obfuscatedCodeWriter.getOutputSourceMapPath('', '');
+            });
+
+            it('should throw an error if output code path is empty', () => {
+                assert.throws(testFunc, Error);
+            });
         });
     });
 });

+ 42 - 41
test/unit-tests/cli/utils/SourceCodeReader.spec.ts

@@ -1,5 +1,6 @@
 import * as fs from 'fs';
 import * as mkdirp from 'mkdirp';
+import * as path from 'path';
 import * as rimraf from 'rimraf';
 
 import { assert } from 'chai';
@@ -11,7 +12,7 @@ import { SourceCodeReader } from '../../../../src/cli/utils/SourceCodeReader';
 describe('SourceCodeReader', () => {
     const expectedError: RegExp = /Given input path must be a valid/;
     const fileContent: string = 'test';
-    const tmpDirectoryPath: string = 'test/tmp';
+    const tmpDirectoryPath: string = path.join('test', 'tmp');
 
     before(() => {
         mkdirp.sync(tmpDirectoryPath);
@@ -21,7 +22,7 @@ describe('SourceCodeReader', () => {
         describe('Variant #1: input path is a file path', () => {
             describe('Variant #1: `inputPath` is a valid path', () => {
                 const tmpFileName: string = 'test.js';
-                const inputPath: string = `${tmpDirectoryPath}/${tmpFileName}`;
+                const inputPath: string = path.join(tmpDirectoryPath, tmpFileName);
                 const expectedFilesData: IFileData[] = [{
                     content: fileContent,
                     filePath: inputPath
@@ -45,7 +46,7 @@ describe('SourceCodeReader', () => {
 
             describe('Variant #2: `inputPath` is not a valid path', () => {
                 const tmpFileName: string = 'test.js';
-                const inputPath: string = `${tmpDirectoryPath}/${tmpFileName}`;
+                const inputPath: string = path.join(tmpDirectoryPath, tmpFileName);
 
                 let testFunc: () => void;
 
@@ -60,7 +61,7 @@ describe('SourceCodeReader', () => {
 
             describe('Variant #3: `inputPath` has invalid extension', () => {
                 const tmpFileName: string = 'test.ts';
-                const inputPath: string = `${tmpDirectoryPath}/${tmpFileName}`;
+                const inputPath: string = path.join(tmpDirectoryPath, tmpFileName);
 
                 let testFunc: () => void;
 
@@ -81,7 +82,7 @@ describe('SourceCodeReader', () => {
             describe('Variant #4: `exclude` option', () => {
                 describe('Variant #1: `inputPath` isn\'t excluded path', () => {
                     const tmpFileName: string = 'test.js';
-                    const inputPath: string = `${tmpDirectoryPath}/${tmpFileName}`;
+                    const inputPath: string = path.join(tmpDirectoryPath, tmpFileName);
                     const expectedFilesData: IFileData[] = [{
                         content: fileContent,
                         filePath: inputPath
@@ -94,7 +95,7 @@ describe('SourceCodeReader', () => {
                         filesData = new SourceCodeReader(
                             inputPath,
                             {
-                                exclude: ['**/foo.js']
+                                exclude: [path.join('**', 'foo.js')]
                             }
                         ).readSourceCode();
                     });
@@ -111,7 +112,7 @@ describe('SourceCodeReader', () => {
                 describe('Variant #2: `inputPath` is excluded path', () => {
                     describe('Variant #1: exclude by `glob` pattern', () => {
                         const tmpFileName: string = 'test.js';
-                        const inputPath: string = `${tmpDirectoryPath}/${tmpFileName}`;
+                        const inputPath: string = path.join(tmpDirectoryPath, tmpFileName);
 
                         let testFunc: () => void;
 
@@ -120,7 +121,7 @@ describe('SourceCodeReader', () => {
                             testFunc = () => new SourceCodeReader(
                                 inputPath,
                                 {
-                                    exclude: [`**/${tmpFileName}`]
+                                    exclude: [path.join('**', tmpFileName)]
                                 }
                             ).readSourceCode();
                         });
@@ -136,7 +137,7 @@ describe('SourceCodeReader', () => {
 
                     describe('Variant #2: exclude by file name', () => {
                         const tmpFileName: string = 'test.js';
-                        const inputPath: string = `${tmpDirectoryPath}/${tmpFileName}`;
+                        const inputPath: string = path.join(tmpDirectoryPath, tmpFileName);
 
                         let testFunc: () => void;
 
@@ -161,7 +162,7 @@ describe('SourceCodeReader', () => {
 
                     describe('Variant #3: exclude by file path', () => {
                         const tmpFileName: string = 'test.js';
-                        const inputPath: string = `${tmpDirectoryPath}/${tmpFileName}`;
+                        const inputPath: string = path.join(tmpDirectoryPath, tmpFileName);
 
                         let testFunc: () => void;
 
@@ -193,10 +194,10 @@ describe('SourceCodeReader', () => {
                 const tmpFileName2: string = 'bar.js';
                 const tmpFileName3: string = 'baz.png';
                 const tmpFileName4: string = 'bark-obfuscated.js';
-                const filePath1: string = `${tmpDirectoryPath}/${tmpFileName1}`;
-                const filePath2: string = `${tmpDirectoryPath}/${tmpFileName2}`;
-                const filePath3: string = `${tmpDirectoryPath}/${tmpFileName3}`;
-                const filePath4: string = `${tmpDirectoryPath}/${tmpFileName4}`;
+                const filePath1: string = path.join(tmpDirectoryPath, tmpFileName1);
+                const filePath2: string = path.join(tmpDirectoryPath, tmpFileName2);
+                const filePath3: string = path.join(tmpDirectoryPath, tmpFileName3);
+                const filePath4: string = path.join(tmpDirectoryPath, tmpFileName4);
 
                 const expectedResult: IFileData[] = [
                     {
@@ -248,16 +249,16 @@ describe('SourceCodeReader', () => {
             describe('Variant #3: `inputPath` is a directory with sub-directories', () => {
                 const parentDirectoryName1: string = 'parent1';
                 const parentDirectoryName2: string = 'parent';
-                const parentDirectoryPath1: string = `${tmpDirectoryPath}/${parentDirectoryName1}`;
-                const parentDirectoryPath2: string = `${tmpDirectoryPath}/${parentDirectoryName2}`;
+                const parentDirectoryPath1: string = path.join(tmpDirectoryPath, parentDirectoryName1);
+                const parentDirectoryPath2: string = path.join(tmpDirectoryPath, parentDirectoryName2);
                 const tmpFileName1: string = 'foo.js';
                 const tmpFileName2: string = 'bar.js';
                 const tmpFileName3: string = 'baz.js';
                 const tmpFileName4: string = 'bark.js';
-                const filePath1: string = `${tmpDirectoryPath}/${tmpFileName1}`;
-                const filePath2: string = `${tmpDirectoryPath}/${tmpFileName2}`;
-                const filePath3: string = `${parentDirectoryPath1}/${tmpFileName3}`;
-                const filePath4: string = `${parentDirectoryPath2}/${tmpFileName4}`;
+                const filePath1: string = path.join(tmpDirectoryPath, tmpFileName1);
+                const filePath2: string = path.join(tmpDirectoryPath, tmpFileName2);
+                const filePath3: string = path.join(parentDirectoryPath1, tmpFileName3);
+                const filePath4: string = path.join(parentDirectoryPath2, tmpFileName4);
 
                 const expectedResult: IFileData[] = [
                     {
@@ -310,10 +311,10 @@ describe('SourceCodeReader', () => {
                     const tmpFileName2: string = 'bar.js';
                     const tmpFileName3: string = 'baz.png';
                     const tmpFileName4: string = 'bark-obfuscated.js';
-                    const filePath1: string = `${tmpDirectoryPath}/${tmpFileName1}`;
-                    const filePath2: string = `${tmpDirectoryPath}/${tmpFileName2}`;
-                    const filePath3: string = `${tmpDirectoryPath}/${tmpFileName3}`;
-                    const filePath4: string = `${tmpDirectoryPath}/${tmpFileName4}`;
+                    const filePath1: string = path.join(tmpDirectoryPath, tmpFileName1);
+                    const filePath2: string = path.join(tmpDirectoryPath, tmpFileName2);
+                    const filePath3: string = path.join(tmpDirectoryPath, tmpFileName3);
+                    const filePath4: string = path.join(tmpDirectoryPath, tmpFileName4);
 
                     const expectedResult: IFileData[] = [
                         {
@@ -359,10 +360,10 @@ describe('SourceCodeReader', () => {
                         const tmpFileName2: string = 'bar.js';
                         const tmpFileName3: string = 'baz.js';
                         const tmpFileName4: string = 'bark.js';
-                        const filePath1: string = `${tmpDirectoryPath}/${tmpFileName1}`;
-                        const filePath2: string = `${tmpDirectoryPath}/${tmpFileName2}`;
-                        const filePath3: string = `${tmpDirectoryPath}/${tmpFileName3}`;
-                        const filePath4: string = `${tmpDirectoryPath}/${tmpFileName4}`;
+                        const filePath1: string = path.join(tmpDirectoryPath, tmpFileName1);
+                        const filePath2: string = path.join(tmpDirectoryPath, tmpFileName2);
+                        const filePath3: string = path.join(tmpDirectoryPath, tmpFileName3);
+                        const filePath4: string = path.join(tmpDirectoryPath, tmpFileName4);
 
                         const expectedResult: IFileData[] = [
                             {
@@ -410,10 +411,10 @@ describe('SourceCodeReader', () => {
                         const tmpFileName2: string = 'bar.js';
                         const tmpFileName3: string = 'baz.js';
                         const tmpFileName4: string = 'bark.js';
-                        const filePath1: string = `${tmpDirectoryPath}/${tmpFileName1}`;
-                        const filePath2: string = `${tmpDirectoryPath}/${tmpFileName2}`;
-                        const filePath3: string = `${tmpDirectoryPath}/${tmpFileName3}`;
-                        const filePath4: string = `${tmpDirectoryPath}/${tmpFileName4}`;
+                        const filePath1: string = path.join(tmpDirectoryPath, tmpFileName1);
+                        const filePath2: string = path.join(tmpDirectoryPath, tmpFileName2);
+                        const filePath3: string = path.join(tmpDirectoryPath, tmpFileName3);
+                        const filePath4: string = path.join(tmpDirectoryPath, tmpFileName4);
 
                         const expectedResult: IFileData[] = [
                             {
@@ -461,10 +462,10 @@ describe('SourceCodeReader', () => {
                         const tmpFileName2: string = 'bar.js';
                         const tmpFileName3: string = 'baz.js';
                         const tmpFileName4: string = 'bark.js';
-                        const filePath1: string = `${tmpDirectoryPath}/${tmpFileName1}`;
-                        const filePath2: string = `${tmpDirectoryPath}/${tmpFileName2}`;
-                        const filePath3: string = `${tmpDirectoryPath}/${tmpFileName3}`;
-                        const filePath4: string = `${tmpDirectoryPath}/${tmpFileName4}`;
+                        const filePath1: string = path.join(tmpDirectoryPath, tmpFileName1);
+                        const filePath2: string = path.join(tmpDirectoryPath, tmpFileName2);
+                        const filePath3: string = path.join(tmpDirectoryPath, tmpFileName3);
+                        const filePath4: string = path.join(tmpDirectoryPath, tmpFileName4);
 
                         const expectedResult: IFileData[] = [
                             {
@@ -512,10 +513,10 @@ describe('SourceCodeReader', () => {
                         const tmpFileName2: string = 'bar.js';
                         const tmpFileName3: string = 'baz.js';
                         const tmpFileName4: string = 'bark.js';
-                        const filePath1: string = `${tmpDirectoryPath}/${tmpFileName1}`;
-                        const filePath2: string = `${tmpDirectoryPath}/${tmpFileName2}`;
-                        const filePath3: string = `${tmpDirectoryPath}/${tmpFileName3}`;
-                        const filePath4: string = `${tmpDirectoryPath}/${tmpFileName4}`;
+                        const filePath1: string = path.join(tmpDirectoryPath, tmpFileName1);
+                        const filePath2: string = path.join(tmpDirectoryPath, tmpFileName2);
+                        const filePath3: string = path.join(tmpDirectoryPath, tmpFileName3);
+                        const filePath4: string = path.join(tmpDirectoryPath, tmpFileName4);
 
                         let testFunc: () => void;
 
@@ -549,7 +550,7 @@ describe('SourceCodeReader', () => {
             describe('Variant #5: `inputPath` is a valid path with dot', () => {
                 const tmpDirectoryWithDotPath: string = `${tmpDirectoryPath}.bar`;
                 const tmpFileName: string = 'foo.js';
-                const filePath: string = `${tmpDirectoryWithDotPath}/${tmpFileName}`;
+                const filePath: string = path.join(tmpDirectoryWithDotPath, tmpFileName);
 
                 const expectedResult: IFileData[] = [
                     {

+ 83 - 67
yarn.lock

@@ -348,10 +348,10 @@
   dependencies:
     type-detect "4.0.8"
 
-"@sinonjs/commons@^1.7.2":
-  version "1.7.2"
-  resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2"
-  integrity sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw==
+"@sinonjs/commons@^1.8.1":
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217"
+  integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==
   dependencies:
     type-detect "4.0.8"
 
@@ -386,10 +386,10 @@
     lodash.get "^4.4.2"
     type-detect "^4.0.8"
 
-"@sinonjs/samsam@^5.1.0":
-  version "5.1.0"
-  resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.1.0.tgz#3afe719232b541bb6cf3411a4c399a188de21ec0"
-  integrity sha512-42nyaQOVunX5Pm6GRJobmzbS7iLI+fhERITnETXzzwDZh+TtDr/Au3yAvXVjFmZ4wEUaE4Y3NFZfKv0bV0cbtg==
+"@sinonjs/samsam@^5.2.0":
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.2.0.tgz#fcff83ab86f83b5498f4a967869c079408d9b5eb"
+  integrity sha512-CaIcyX5cDsjcW/ab7HposFWzV1kC++4HNsfnEdFJa7cP1QIuILAKV+BgfeqRXhcnSAc76r/Rh/O5C+300BwUIw==
   dependencies:
     "@sinonjs/commons" "^1.6.0"
     lodash.get "^4.4.2"
@@ -400,10 +400,10 @@
   resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
   integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
 
-"@types/[email protected]2":
-  version "4.2.12"
-  resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.12.tgz#6160ae454cd89dae05adc3bb97997f488b608201"
-  integrity sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ==
+"@types/[email protected]3":
+  version "4.2.13"
+  resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.13.tgz#8a3801f6655179d1803d81e94a2e4aaf317abd16"
+  integrity sha512-o3SGYRlOpvLFpwJA6Sl1UPOwKFEvE4FxTEB/c9XHI2whdnd4kmPVkNLL8gY4vWGBxWWDumzLbKsAhEH5SKn37Q==
 
 "@types/[email protected]":
   version "1.1.0"
@@ -518,10 +518,10 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.3.tgz#6356df2647de9eac569f9a52eda3480fa9e70b4d"
   integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA==
 
-"@types/[email protected].2":
-  version "14.11.2"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256"
-  integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==
+"@types/[email protected].5":
+  version "14.11.5"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.5.tgz#fecad41c041cae7f2404ad4b2d0742fdb628b305"
+  integrity sha512-jVFzDV6NTbrLMxm4xDSIW/gKnk8rQLF9wAzLWIOg+5nU6ACrIMndeBdXci0FGtqJbP9tQvm6V39eshc96TO2wQ==
 
 "@types/normalize-package-data@^2.4.0":
   version "2.4.0"
@@ -568,61 +568,61 @@
   resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.15.3.tgz#fb602cd4c2f0b7c0fb857e922075fdf677d25d84"
   integrity sha512-5oiXqR7kwDGZ6+gmzIO2lTC+QsriNuQXZDWNYRV3l2XRN/zmPgnC21DLSx2D05zvD8vnXW6qUg7JnXZ4I6qLVQ==
 
-"@typescript-eslint/eslint-plugin@4.3.0":
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.3.0.tgz#1a23d904bf8ea248d09dc3761af530d90f39c8fa"
-  integrity sha512-RqEcaHuEKnn3oPFislZ6TNzsBLqpZjN93G69SS+laav/I8w/iGMuMq97P0D2/2/kW4SCebHggqhbcCfbDaaX+g==
+"@typescript-eslint/eslint-plugin@4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.4.0.tgz#0321684dd2b902c89128405cf0385e9fe8561934"
+  integrity sha512-RVt5wU9H/2H+N/ZrCasTXdGbUTkbf7Hfi9eLiA8vPQkzUJ/bLDCC3CsoZioPrNcnoyN8r0gT153dC++A4hKBQQ==
   dependencies:
-    "@typescript-eslint/experimental-utils" "4.3.0"
-    "@typescript-eslint/scope-manager" "4.3.0"
+    "@typescript-eslint/experimental-utils" "4.4.0"
+    "@typescript-eslint/scope-manager" "4.4.0"
     debug "^4.1.1"
     functional-red-black-tree "^1.0.1"
     regexpp "^3.0.0"
     semver "^7.3.2"
     tsutils "^3.17.1"
 
-"@typescript-eslint/experimental-utils@4.3.0":
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.3.0.tgz#3f3c6c508e01b8050d51b016e7f7da0e3aefcb87"
-  integrity sha512-cmmIK8shn3mxmhpKfzMMywqiEheyfXLV/+yPDnOTvQX/ztngx7Lg/OD26J8gTZfkLKUmaEBxO2jYP3keV7h2OQ==
+"@typescript-eslint/experimental-utils@4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.4.0.tgz#62a05d3f543b8fc5dec4982830618ea4d030e1a9"
+  integrity sha512-01+OtK/oWeSJTjQcyzDztfLF1YjvKpLFo+JZmurK/qjSRcyObpIecJ4rckDoRCSh5Etw+jKfdSzVEHevh9gJ1w==
   dependencies:
     "@types/json-schema" "^7.0.3"
-    "@typescript-eslint/scope-manager" "4.3.0"
-    "@typescript-eslint/types" "4.3.0"
-    "@typescript-eslint/typescript-estree" "4.3.0"
+    "@typescript-eslint/scope-manager" "4.4.0"
+    "@typescript-eslint/types" "4.4.0"
+    "@typescript-eslint/typescript-estree" "4.4.0"
     eslint-scope "^5.0.0"
     eslint-utils "^2.0.0"
 
-"@typescript-eslint/parser@4.3.0":
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.3.0.tgz#684fc0be6551a2bfcb253991eec3c786a8c063a3"
-  integrity sha512-JyfRnd72qRuUwItDZ00JNowsSlpQGeKfl9jxwO0FHK1qQ7FbYdoy5S7P+5wh1ISkT2QyAvr2pc9dAemDxzt75g==
+"@typescript-eslint/parser@4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.4.0.tgz#65974db9a75f23b036f17b37e959b5f99b659ec0"
+  integrity sha512-yc14iEItCxoGb7W4Nx30FlTyGpU9r+j+n1LUK/exlq2eJeFxczrz/xFRZUk2f6yzWfK+pr1DOTyQnmDkcC4TnA==
   dependencies:
-    "@typescript-eslint/scope-manager" "4.3.0"
-    "@typescript-eslint/types" "4.3.0"
-    "@typescript-eslint/typescript-estree" "4.3.0"
+    "@typescript-eslint/scope-manager" "4.4.0"
+    "@typescript-eslint/types" "4.4.0"
+    "@typescript-eslint/typescript-estree" "4.4.0"
     debug "^4.1.1"
 
-"@typescript-eslint/scope-manager@4.3.0":
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.3.0.tgz#c743227e087545968080d2362cfb1273842cb6a7"
-  integrity sha512-cTeyP5SCNE8QBRfc+Lgh4Xpzje46kNUhXYfc3pQWmJif92sjrFuHT9hH4rtOkDTo/si9Klw53yIr+djqGZS1ig==
+"@typescript-eslint/scope-manager@4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.4.0.tgz#2f3dd27692a12cc9a046a90ba6a9d8cb7731190a"
+  integrity sha512-r2FIeeU1lmW4K3CxgOAt8djI5c6Q/5ULAgdVo9AF3hPMpu0B14WznBAtxrmB/qFVbVIB6fSx2a+EVXuhSVMEyA==
   dependencies:
-    "@typescript-eslint/types" "4.3.0"
-    "@typescript-eslint/visitor-keys" "4.3.0"
+    "@typescript-eslint/types" "4.4.0"
+    "@typescript-eslint/visitor-keys" "4.4.0"
 
-"@typescript-eslint/types@4.3.0":
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.3.0.tgz#1f0b2d5e140543e2614f06d48fb3ae95193c6ddf"
-  integrity sha512-Cx9TpRvlRjOppGsU6Y6KcJnUDOelja2NNCX6AZwtVHRzaJkdytJWMuYiqi8mS35MRNA3cJSwDzXePfmhU6TANw==
+"@typescript-eslint/types@4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.4.0.tgz#63440ef87a54da7399a13bdd4b82060776e9e621"
+  integrity sha512-nU0VUpzanFw3jjX+50OTQy6MehVvf8pkqFcURPAE06xFNFenMj1GPEI6IESvp7UOHAnq+n/brMirZdR+7rCrlA==
 
-"@typescript-eslint/typescript-estree@4.3.0":
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.3.0.tgz#0edc1068e6b2e4c7fdc54d61e329fce76241cee8"
-  integrity sha512-ZAI7xjkl+oFdLV/COEz2tAbQbR3XfgqHEGy0rlUXzfGQic6EBCR4s2+WS3cmTPG69aaZckEucBoTxW9PhzHxxw==
+"@typescript-eslint/typescript-estree@4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.4.0.tgz#16a2df7c16710ddd5406b32b86b9c1124b1ca526"
+  integrity sha512-Fh85feshKXwki4nZ1uhCJHmqKJqCMba+8ZicQIhNi5d5jSQFteWiGeF96DTjO8br7fn+prTP+t3Cz/a/3yOKqw==
   dependencies:
-    "@typescript-eslint/types" "4.3.0"
-    "@typescript-eslint/visitor-keys" "4.3.0"
+    "@typescript-eslint/types" "4.4.0"
+    "@typescript-eslint/visitor-keys" "4.4.0"
     debug "^4.1.1"
     globby "^11.0.1"
     is-glob "^4.0.1"
@@ -630,12 +630,12 @@
     semver "^7.3.2"
     tsutils "^3.17.1"
 
-"@typescript-eslint/visitor-keys@4.3.0":
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.3.0.tgz#0e5ab0a09552903edeae205982e8521e17635ae0"
-  integrity sha512-xZxkuR7XLM6RhvLkgv9yYlTcBHnTULzfnw4i6+z2TGBLy9yljAypQaZl9c3zFvy7PNI7fYWyvKYtohyF8au3cw==
+"@typescript-eslint/visitor-keys@4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.4.0.tgz#0a9118344082f14c0f051342a74b42dfdb012640"
+  integrity sha512-oBWeroUZCVsHLiWRdcTXJB7s1nB3taFY8WGvS23tiAlT6jXVvsdAV4rs581bgdEjOhn43q6ro7NkOiLKu6kFqA==
   dependencies:
-    "@typescript-eslint/types" "4.3.0"
+    "@typescript-eslint/types" "4.4.0"
     eslint-visitor-keys "^2.0.0"
 
 "@webassemblyjs/[email protected]":
@@ -1710,6 +1710,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
[email protected]:
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9"
+  integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw==
+  dependencies:
+    cross-spawn "^7.0.1"
+
 cross-spawn@^5.0.1:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -1739,6 +1746,15 @@ cross-spawn@^7.0.0:
     shebang-command "^2.0.0"
     which "^2.0.1"
 
+cross-spawn@^7.0.1:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+  dependencies:
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
+
 cross-spawn@^7.0.2:
   version "7.0.2"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6"
@@ -5026,15 +5042,15 @@ signal-exit@^3.0.2:
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
   integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
 
-sinon@9.1.0:
-  version "9.1.0"
-  resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.1.0.tgz#4afc90707c8e360fe051398eed2d3b197980ffc3"
-  integrity sha512-9zQShgaeylYH6qtsnNXlTvv0FGTTckuDfHBi+qhgj5PvW2r2WslHZpgc3uy3e/ZAoPkqaOASPi+juU6EdYRYxA==
+sinon@9.2.0:
+  version "9.2.0"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.0.tgz#1d333967e30023609f7347351ebc0dc964c0f3c9"
+  integrity sha512-eSNXz1XMcGEMHw08NJXSyTHIu6qTCOiN8x9ODACmZpNQpr0aXTBXBnI4xTzQzR+TEpOmLiKowGf9flCuKIzsbw==
   dependencies:
-    "@sinonjs/commons" "^1.7.2"
+    "@sinonjs/commons" "^1.8.1"
     "@sinonjs/fake-timers" "^6.0.1"
     "@sinonjs/formatio" "^5.0.1"
-    "@sinonjs/samsam" "^5.1.0"
+    "@sinonjs/samsam" "^5.2.0"
     diff "^4.0.2"
     nise "^4.0.4"
     supports-color "^7.1.0"
@@ -5567,10 +5583,10 @@ tsconfig-paths@^3.9.0:
     minimist "^1.2.0"
     strip-bom "^3.0.0"
 
[email protected].1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"
-  integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==
[email protected].2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.2.tgz#462295631185db44b21b1ea3615b63cd1c038242"
+  integrity sha512-wAH28hcEKwna96/UacuWaVspVLkg4x1aDM9JlzqaQTOFczCktkVAb5fmXChgandR1EraDPs2w8P+ozM+oafwxg==
 
 tslib@>=1.9.0, tslib@^1.8.1, tslib@^1.9.0:
   version "1.11.1"

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.