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

Issue:
TypeError: [[rawConfiguration.extends],[reportedConfiguration.extends]].flat is not a function
<!--
👋 Hi, thanks for filing an issue on tslint-to-eslint-config! 💖
Please fill out all fields below to ensure your issue is addressed.

If your issue doesn't provide enough info to fully explain or reproduce your bug, it will be closed. 😦
-->

### 🐛 Bug Report

-   `tslint-to-eslint-config` version: 0.2.11
-   TSLint version: 5.18.0
-   ESLint version: none?
-   Node version: 10.17.0

#### Actual Behavior

<!-- What is the behavior right now? -->

1. I generated an empty angular project with `ng new`, `ng 8.1.3`.
2. I updated manually tslint from 5.15 to 5.18 since this is the minimum required version
3. Then I ran `npx tslint-to-eslint-config` and expected it to migrate from tslint to eslint

But I got this error:

```
npx: installed 41 in 3.972s
❌ 1 error running tslint-to-eslint: ❌
TypeError: [[rawConfiguration.extends],[reportedConfiguration.extends]].flat is not a function
    at exports.findTSLintConfiguration (/home/user/.npm/_npx/19151/lib/node_modules/tslint-to-eslint-config/src/input/findTSLintConfiguration.js:22:10)
    at process._tickCallback (internal/process/next_tick.js:68:7)
```

#### Expected Behavior

<!-- What are the specific changes you'd like to see? -->
I'd expected it to migrate to `eslint` smoothly.

#### Reproduction

<!--
Please paste a link to a repository, Gist, or other means of reproducing your error here.
Note that `tslint-to-eslint-config` includes information from `package.json`, `tsconfig.json`, and other files, so just your ESLint configuration may not be enough.
-->
`tslint-to-eslint-config` is empty.

Pull request description:
Lowered lib version to support Node 10
## PR Checklist

-   [x] Addresses an existing issue: fixes #264
-   [x] That issue was marked as [`status: accepting prs`](https://github.com/typescript-eslint/tslint-to-eslint-config/labels/status%3A%20accepting%20prs)

## Overview

Lowers the TypeScript lib target down to `es2017` to make sure 2018+ features not supported by Node 10 aren't added.

Patch:
diff --git a/src/creation/formatting/formatters/withKeysSorted.ts b/src/creation/formatting/formatters/withKeysSorted.ts
index b0fad6b..ea5975f 100644
--- a/src/creation/formatting/formatters/withKeysSorted.ts
+++ b/src/creation/formatting/formatters/withKeysSorted.ts
@@ -1,7 +1,10 @@
 export const withKeysSorted = (input: any) => {
-    return Object.fromEntries(
-        Object.keys(input)
-            .sort((a, b) => a.localeCompare(b))
-            .map(key => [key, input[key]]),
-    );
+    const output: Record<string, any> = {};
+    const keys = Object.keys(input).sort((a, b) => a.localeCompare(b));
+
+    for (const key of keys) {
+        output[key] = input[key];
+    }
+
+    return output;
 };
diff --git a/src/input/findESLintConfiguration.ts b/src/input/findESLintConfiguration.ts
index 3704bb6..0a3f252 100644
--- a/src/input/findESLintConfiguration.ts
+++ b/src/input/findESLintConfiguration.ts
@@ -2,6 +2,7 @@ import { Exec } from "../adapters/exec";
 import { SansDependencies } from "../binding";
 import { ESLintRuleSeverity } from "../rules/types";
 import { TSLintToESLintSettings } from "../types";
+import { uniqueFromSources } from "../utils";
 import { findRawConfiguration } from "./findRawConfiguration";
 import { findReportedConfiguration } from "./findReportedConfiguration";
 import { OriginalConfigurations } from "./findOriginalConfigurations";
@@ -60,9 +61,7 @@ export const findESLintConfiguration = async (
         return reportedConfiguration;
     }
 
-    const extensions = [rawConfiguration.extends, [reportedConfiguration.extends || []]].flat(
-        Infinity,
-    );
+    const extensions = uniqueFromSources(rawConfiguration.extends, reportedConfiguration.extends);
 
     return {
         full: {
diff --git a/src/input/findTSLintConfiguration.ts b/src/input/findTSLintConfiguration.ts
index 1fe4324..7777255 100644
--- a/src/input/findTSLintConfiguration.ts
+++ b/src/input/findTSLintConfiguration.ts
@@ -2,8 +2,8 @@ import { findRawConfiguration } from "./findRawConfiguration";
 import { findReportedConfiguration } from "./findReportedConfiguration";
 import { Exec } from "../adapters/exec";
 import { SansDependencies } from "../binding";
+import { uniqueFromSources } from "../utils";
 import { importer } from "./importer";
-import { isDefined } from "../utils";
 
 export type TSLintConfiguration = {
     extends?: string[];
@@ -44,13 +44,7 @@ export const findTSLintConfiguration = async (
         return rawConfiguration;
     }
 
-    const extensions = Array.from(
-        new Set(
-            [[rawConfiguration.extends], [reportedConfiguration.extends]]
-                .flat(Infinity)
-                .filter(isDefined),
-        ),
-    );
+    const extensions = uniqueFromSources(rawConfiguration.extends, reportedConfiguration.extends);
 
     const rules = {
         ...rawConfiguration.rules,
diff --git a/src/utils.ts b/src/utils.ts
index 8deffdc..65139d3 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -7,3 +7,17 @@ export type RemoveErrors<Items> = {
 };
 
 export type PromiseValue<T> = T extends Promise<infer R> ? R : never;
+
+export const uniqueFromSources = <T>(...sources: (T | T[] | undefined)[]) => {
+    const items: T[] = [];
+
+    for (const source of sources) {
+        if (source instanceof Array) {
+            items.push(...source.filter(isDefined));
+        } else if (source !== undefined) {
+            items.push(source);
+        }
+    }
+
+    return Array.from(new Set(items));
+};
diff --git a/tsconfig.json b/tsconfig.json
index d836d02..b7b75c8 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -4,7 +4,7 @@
         "declaration": true,
         "esModuleInterop": true,
         "incremental": true,
-        "lib": ["esnext"],
+        "lib": ["es2017"],
         "module": "commonjs",
         "noFallthroughCasesInSwitch": true,
         "noImplicitAny": true,


Test patch:
diff --git a/src/utils.test.ts b/src/utils.test.ts
index 11417a9..f0180a1 100644
--- a/src/utils.test.ts
+++ b/src/utils.test.ts
@@ -1,4 +1,4 @@
-import { isDefined, isError } from "./utils";
+import { isDefined, isError, uniqueFromSources } from "./utils";
 
 describe("isDefined", () => {
     it("returns true when the item is defined", () => {
@@ -47,3 +47,38 @@ describe("isError", () => {
         expect(result).toBe(false);
     });
 });
+
+describe("uniqueFromSources", () => {
+    it("returns unique items when multiple are given", () => {
+        // Arange
+        const sources = ["a", "b", "b", "c"];
+
+        // Act
+        const result = uniqueFromSources(...sources);
+
+        // Assert
+        expect(result).toEqual(["a", "b", "c"]);
+    });
+
+    it("returns items from a nested array", () => {
+        // Arange
+        const sources = ["a", ["b"], "c"];
+
+        // Act
+        const result = uniqueFromSources(...sources);
+
+        // Assert
+        expect(result).toEqual(["a", "b", "c"]);
+    });
+
+    it("filters out undefined inputs", () => {
+        // Arange
+        const sources = ["a", "b", "c", undefined];
+
+        // Act
+        const result = uniqueFromSources(...sources);
+
+        // Assert
+        expect(result).toEqual(["a", "b", "c"]);
+    });
+});