Skip to content

Commit

Permalink
Add command for installing TypeScript (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephen Hanson authored May 16, 2023
1 parent 0c40068 commit 50aa180
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 4 deletions.
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@
"author": "Stephen Hanson",
"license": "MIT",
"dependencies": {
"chalk": "^5.2.0",
"commander": "^10.0.1",
"inquirer": "^9.2.0"
"eta": "^2.1.1",
"fs-extra": "^11.1.1",
"inquirer": "^9.2.0",
"ora": "^6.3.0",
"prettier": "^2.8.8"
},
"devDependencies": {
"@types/fs-extra": "^11.0.1",
"@types/node": "^18.16.3",
"@types/react": "^18.2.6",
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
}
Expand Down
7 changes: 5 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { program } from "commander";
import buildAction from "./util/buildAction";

console.log("React Native 🎉");

export default function runCli() {
program
.name("thoughtbelt")
Expand All @@ -19,6 +17,11 @@ export default function runCli() {
.description("Configure Prettier")
.action(buildAction(import("./commands/prettier")));

program
.command("typescript")
.description("Install and configure TypeScript")
.action(buildAction(import("./commands/typescript")));

program.showHelpAfterError().parse();
}

Expand Down
29 changes: 29 additions & 0 deletions src/commands/templates/tsconfig.json.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "esNext",
"lib": ["esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"baseUrl": ".",
"jsx": "react-native",
"noFallthroughCasesInSwitch": true,
"paths": {
"src/*": ["src/*"],
"assets/*": ["assets/*"]
}
},
"include": ["src/**/*", "*.js", ".*.js"],
"exclude": ["node_modules"],
<% if(it.expo) { %>
"extends": "expo/tsconfig.base"
<% } %>
}
46 changes: 46 additions & 0 deletions src/commands/typescript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import chalk from "chalk";
import { log } from "console";
import * as eta from "eta";
import fs from "fs-extra";
import { fileURLToPath, URL } from "url";
import path from "path";
import addDependency from "../util/addDependency";
import getProjectDir from "../util/getProjectDir";
import getProjectType from "../util/getProjectType";
import writeFile from "../util/writeFile";

// for manual testing, change this to another name so doesn't conflict
// with project's tsconfig.json
const tsConfig = "tsconfig.json";
const __dirname = fileURLToPath(new URL(".", import.meta.url));

export default async function addTypescript() {
const projectDir = await getProjectDir();

if (await fs.exists(path.join(projectDir, tsConfig))) {
log(
chalk.yellow("tsconfig.json already exists, exiting.") +
"\nIf you would like to perform a fresh TypeScript install, delete this file and rerun the script.\n"
);
return;
}

addDependency("typescript @types/react", { dev: true });

const projectType = await getProjectType();
const template = await fs.readFile(
path.join(__dirname, "templates", "tsconfig.json.eta")
);
const fileContents = eta.render(template.toString(), {
expo: projectType === "expo-bare" || projectType === "expo-managed",
});

await writeFile(path.join(projectDir, tsConfig), fileContents, {
format: true,
});

log(
chalk.green("\n🎉 TypeScript successfully configured") +
"\nConsider renaming your existing JS files as .ts or .tsx.\n"
);
}
14 changes: 14 additions & 0 deletions src/util/addDependency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as fs from "fs-extra";
import getProjectDir from "./getProjectDir";
import * as path from "path";
import { exec } from "child_process";

export default async function addDependency(deps: string, { dev = false }) {
const isYarn = await fs.exists(path.join(await getProjectDir(), "yarn.lock"));

if (isYarn) {
exec(`yarn add ${dev ? "--dev" : ""} ${deps}`);
} else {
exec(`npm install ${dev ? "--save-dev" : "--save"} ${deps}`);
}
}
5 changes: 5 additions & 0 deletions src/util/formatFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { exec } from "child_process";

export default async function formatFile(filePath: string) {
exec(`npx prettier --write '${filePath}'`);
}
28 changes: 28 additions & 0 deletions src/util/getProjectDir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import fs from "fs-extra";
import path from "path";

export default async function getProjectDir(
base: string = process.cwd()
): Promise<string> {
let previous = null;
let dir = base;

do {
try {
// This will throw if there is no package.json in the directory
await fs.readFile(path.join(dir, "package.json"));

// if didn't throw, package.json exists, return dir
return dir;
} catch {
// Expected to throw if no package.json is present
} finally {
previous = dir;
dir = path.dirname(dir);
}
} while (dir !== previous);

throw new Error(
"No project found. Ensure you are inside of a project directory with a package.json file."
);
}
16 changes: 16 additions & 0 deletions src/util/getProjectType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import readPackageJson from "./readPackageJson";

type ProjectType = "expo-bare" | "expo-managed" | "react-native";

export default async function getProjectType(): Promise<ProjectType> {
const packageJson = await readPackageJson();
const hasExpo = packageJson.dependencies?.hasOwnProperty("expo");
const hasReactNativeUnimodules = packageJson.dependencies?.hasOwnProperty(
"react-native-unimodules"
);
if (hasExpo) {
return hasReactNativeUnimodules ? "expo-bare" : "expo-managed";
}

return "react-native";
}
9 changes: 9 additions & 0 deletions src/util/readPackageJson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import path from "path";
import getProjectDir from "./getProjectDir";
import fs from "fs-extra";

export default async function readPackageJson() {
const rootDir = await getProjectDir();
const pkg = await fs.readFile(path.join(rootDir, "package.json"));
return JSON.parse(pkg.toString());
}
20 changes: 20 additions & 0 deletions src/util/writeFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import chalk from "chalk";
import fs from "fs-extra";
import formatFile from "./formatFile";

type Options = {
format?: boolean;
};

export default async function writeFile(
filePath: string,
contents: string,
{ format = false }: Options = {}
) {
console.log(chalk.bold(`🔨 Creating ${filePath}`));
fs.writeFile(filePath, contents);

if (format) {
await formatFile(filePath);
}
}
84 changes: 83 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,50 @@
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==

"@types/fs-extra@^11.0.1":
version "11.0.1"
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.1.tgz#f542ec47810532a8a252127e6e105f487e0a6ea5"
integrity sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==
dependencies:
"@types/jsonfile" "*"
"@types/node" "*"

"@types/jsonfile@*":
version "6.1.1"
resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.1.tgz#ac84e9aefa74a2425a0fb3012bdea44f58970f1b"
integrity sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==
dependencies:
"@types/node" "*"

"@types/node@*":
version "20.1.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.1.1.tgz#afc492e8dbe7f672dd3a13674823522b467a45ad"
integrity sha512-uKBEevTNb+l6/aCQaKVnUModfEMjAl98lw2Si9P5y4hLu9tm6AlX2ZIoXZX6Wh9lJueYPrGPKk5WMCNHg/u6/A==

"@types/node@^18.16.3":
version "18.16.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01"
integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==

"@types/prop-types@*":
version "15.7.5"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==

"@types/react@^18.2.6":
version "18.2.6"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.6.tgz#5cd53ee0d30ffc193b159d3516c8c8ad2f19d571"
integrity sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"

"@types/scheduler@*":
version "0.16.3"
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5"
integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==

acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
Expand Down Expand Up @@ -148,6 +187,11 @@ create-require@^1.1.0:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==

csstype@^3.0.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==

defaults@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a"
Expand Down Expand Up @@ -175,6 +219,11 @@ escape-string-regexp@^5.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==

eta@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/eta/-/eta-2.1.1.tgz#dfc1ced5e5515211cb9c6c9fac3eed86f4cce3e1"
integrity sha512-daWDLvwOCghtrXRVI4LEt57/R3hFwCidxRlczeYUGBWZ/8MuyZiusOFiCAXkVziuBMdku/lKQk2BgH1hsmijbA==

external-editor@^3.0.3:
version "3.1.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
Expand All @@ -192,6 +241,20 @@ figures@^5.0.0:
escape-string-regexp "^5.0.0"
is-unicode-supported "^1.2.0"

fs-extra@^11.1.1:
version "11.1.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d"
integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"

graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==

iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
Expand Down Expand Up @@ -240,6 +303,15 @@ is-unicode-supported@^1.1.0, is-unicode-supported@^1.2.0:
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714"
integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==

jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
dependencies:
universalify "^2.0.0"
optionalDependencies:
graceful-fs "^4.1.6"

lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
Expand Down Expand Up @@ -275,7 +347,7 @@ onetime@^5.1.0:
dependencies:
mimic-fn "^2.1.0"

ora@^6.1.2:
ora@^6.1.2, ora@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/ora/-/ora-6.3.0.tgz#a314600999f514a989a0904f5c17c8b7c1f7c878"
integrity sha512-1/D8uRFY0ay2kgBpmAwmSA404w4OoPVhHMqRqtjvrcK/dnzcEZxMJ+V4DUbyICu8IIVRclHcOf5wlD1tMY4GUQ==
Expand All @@ -295,6 +367,11 @@ os-tmpdir@~1.0.2:
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==

prettier@^2.8.8:
version "2.8.8"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==

readable-stream@^3.4.0:
version "3.6.2"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
Expand Down Expand Up @@ -415,6 +492,11 @@ typescript@^5.0.4:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==

universalify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==

util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
Expand Down

0 comments on commit 50aa180

Please sign in to comment.