Repository: typescript-eslint/tslint-to-eslint-config (ts)

Issue:
Missing mergers: no-caller, no-eval
### 🐛 Bug Report

-   `tslint-to-eslint-config` version: dunno. used npx to run
-   ESLint version: 5.16.0
-   Node version: 8.16.0

#### Actual Behavior

my eslint config is not updated

**console output**
✨ 101 rules replaced with their ESLint equivalents. ✨
📢 4 ESLint rules behave differently from their TSLint counterparts: 📢
* no-invalid-this:
  - Functions in methods will no longer be ignored.
* one-var:
  - Variables declared in for loops will no longer be checked.
* no-plusplus:
  - ++ and -- operators will now only be allowed in for loops.
* class-methods-use-this:
  - allow-protected methods will no longer be ignored.
💀 2 errors thrown. 💀
Check ./tslint-to-eslint-config.log for details.
️👀 132 rules do not yet have ESLint equivalents; defaulting to eslint-plugin-tslint. 👀
✅ All is well! ✅

**log file**
no-eval threw an error during conversion: Error: No merger for multiple output no-eval rule configurations.
    at exports.convertRules (...\npm-cache\_npx\14132\node_modules\tslint-to-eslint-config\src\rules\convertRules.js:37:67)
    at exports.convertConfig (...\npm-cache\_npx\14132\node_modules\tslint-to-eslint-config\src\conversion\convertConfig.js:15:48)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:189:7)
no-arg threw an error during conversion: Error: No merger for multiple output no-caller rule configurations.
    at exports.convertRules (...\npm-cache\_npx\14132\node_modules\tslint-to-eslint-config\src\rules\convertRules.js:37:67)
    at exports.convertConfig (...\npm-cache\_npx\14132\node_modules\tslint-to-eslint-config\src\conversion\convertConfig.js:15:48)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:189:7)

#### Expected Behavior

eslint config would be updated

#### Reproduction

Can you reply to this issue and let me know how much help you need to repro? I'd prefer not to just copy config/details from an internal repo.


Pull request description:
Add no-caller and no-eval mergers
<!--
👋 Hi, thanks for sending a PR to tslint-to-eslint-config! 💖
Please fill out all fields below to ensure your PR is reviewed quickly.
-->

## PR Checklist

-   [x] Addresses an existing issue: fixes #135
-   [x] That issue was marked as [`status: accepting prs`](https://github.com/typescript-eslint/tslint-to-eslint-config/labels/status%3A%20accepting%20prs)

## Overview
- `no-caller` is quite straightforward as it has no options.
- For `no-eval` I only set `allowIndirect: true` if both original and new options have it set to true, because otherwise at least one of the original TSLint rules would have flagged indirect `eval` calls out.

ESLint Docs
- [`no-caller`](https://eslint.org/docs/rules/no-caller)
- [`no-eval`](https://eslint.org/docs/rules/no-eval)

Patch:
diff --git a/src/rules/mergers.ts b/src/rules/mergers.ts
index 583f3d4..d323224 100644
--- a/src/rules/mergers.ts
+++ b/src/rules/mergers.ts
@@ -1,7 +1,11 @@
 import { mergeBanTypes } from "./mergers/ban-types";
+import { mergeNoCaller } from "./mergers/no-caller";
+import { mergeNoEval } from "./mergers/no-eval";
 import { mergeNoUnnecessaryTypeAssertion } from "./mergers/no-unnecessary-type-assertion";
 
 export const mergers = new Map([
     ["@typescript-eslint/ban-types", mergeBanTypes],
     ["@typescript-eslint/no-unnecessary-type-assertion", mergeNoUnnecessaryTypeAssertion],
+    ["no-caller", mergeNoCaller],
+    ["no-eval", mergeNoEval],
 ]);
diff --git a/src/rules/mergers/no-caller.ts b/src/rules/mergers/no-caller.ts
new file mode 100644
index 0000000..1d2450f
--- /dev/null
+++ b/src/rules/mergers/no-caller.ts
@@ -0,0 +1,6 @@
+import { RuleMerger } from "../merger";
+
+export const mergeNoCaller: RuleMerger = () => {
+    // no-caller rule does not accept any options
+    return [];
+};
diff --git a/src/rules/mergers/no-eval.ts b/src/rules/mergers/no-eval.ts
new file mode 100644
index 0000000..59cf5af
--- /dev/null
+++ b/src/rules/mergers/no-eval.ts
@@ -0,0 +1,28 @@
+import { RuleMerger } from "../merger";
+
+export const mergeNoEval: RuleMerger = (existingOptions, newOptions) => {
+    if (existingOptions === undefined && newOptions === undefined) {
+        return [];
+    }
+
+    let allowIndirect = true;
+
+    for (const options of [existingOptions, newOptions]) {
+        if (
+            options === undefined ||
+            options.length === 0 ||
+            options[0].allowIndirect === undefined
+        ) {
+            allowIndirect = false;
+            break;
+        }
+
+        allowIndirect = allowIndirect && options[0].allowIndirect;
+    }
+
+    return [
+        {
+            ...(allowIndirect && { allowIndirect }),
+        },
+    ];
+};


Test patch:
diff --git a/src/rules/mergers/tests/no-caller.test.ts b/src/rules/mergers/tests/no-caller.test.ts
new file mode 100644
index 0000000..adee30e
--- /dev/null
+++ b/src/rules/mergers/tests/no-caller.test.ts
@@ -0,0 +1,9 @@
+import { mergeNoCaller } from "../no-caller";
+
+describe(mergeNoCaller, () => {
+    test("neither options existing", () => {
+        const result = mergeNoCaller(undefined, undefined);
+
+        expect(result).toEqual([]);
+    });
+});
diff --git a/src/rules/mergers/tests/no-eval.test.ts b/src/rules/mergers/tests/no-eval.test.ts
new file mode 100644
index 0000000..a47a581
--- /dev/null
+++ b/src/rules/mergers/tests/no-eval.test.ts
@@ -0,0 +1,45 @@
+import { mergeNoEval } from "../no-eval";
+
+describe(mergeNoEval, () => {
+    test("neither options existing", () => {
+        const result = mergeNoEval(undefined, undefined);
+
+        expect(result).toEqual([]);
+    });
+
+    test("neither allowIndirect existing", () => {
+        const result = mergeNoEval([{}], [{}]);
+
+        expect(result).toEqual([{}]);
+    });
+
+    test("original allowIndirect existing", () => {
+        const result = mergeNoEval([{ allowIndirect: true }], [{}]);
+
+        expect(result).toEqual([{}]);
+    });
+
+    test("new allowIndirect existing", () => {
+        const result = mergeNoEval([{}], [{ allowIndirect: true }]);
+
+        expect(result).toEqual([{}]);
+    });
+
+    test("original allowIndirect is false but new allowIndirect is true", () => {
+        const result = mergeNoEval([{ allowIndirect: false }], [{ allowIndirect: true }]);
+
+        expect(result).toEqual([{}]);
+    });
+
+    test("original allowIndirect is true but new allowIndirect is false", () => {
+        const result = mergeNoEval([{ allowIndirect: true }], [{ allowIndirect: false }]);
+
+        expect(result).toEqual([{}]);
+    });
+
+    test("both allowIndirect are true", () => {
+        const result = mergeNoEval([{ allowIndirect: true }], [{ allowIndirect: true }]);
+
+        expect(result).toEqual([{ allowIndirect: true }]);
+    });
+});