Skip to content

Commit

Permalink
Add ESLint to project, fix lint issues, set up CI (#12)
Browse files Browse the repository at this point in the history
* Add ESLint to project, fix lint issues

* Configure CI
  • Loading branch information
stevehanson committed Jul 18, 2023
1 parent a65185f commit 62fb3db
Show file tree
Hide file tree
Showing 11 changed files with 2,082 additions and 22 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: thoughtbelt CI

on:
push:
branches: [main]
pull_request:

jobs:
build:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [14.x, 16.x, 18.x, 20.x]

steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: yarn --frozen-lockfile
- name: Run linter
run: yarn lint
- name: Run tests
run: yarn test
4 changes: 2 additions & 2 deletions bin/belt.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { spawn } from "node:child_process";
import path from "node:path";
import { URL, fileURLToPath } from "node:url";

const __dirname = fileURLToPath(new URL(".", import.meta.url));
const dirname = fileURLToPath(new URL(".", import.meta.url));
const args = process.argv.slice(2);

spawn("ts-node", [path.join(__dirname, "../src/cli.ts"), ...args], {
spawn("ts-node", [path.join(dirname, "../src/cli.ts"), ...args], {
stdio: "inherit",
});
15 changes: 14 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@
"type": "module",
"scripts": {
"belt": "bin/belt.js",
"test": "echo \"Error: no test specified\" && exit 1"
"lint": "eslint --max-warnings=0 --ext js,jsx,ts,tsx .",
"test": "echo \"testing not yet configured\""
},
"bin": {
"thoughtbelt": "./bin/belt.js"
},
"author": "thoughtbot, Inc.",
"license": "MIT",
"dependencies": {
"@thoughtbot/eslint-config": "thoughtbot/eslint-config#eslint-configs",
"chalk": "^5.2.0",
"commander": "^10.0.1",
"eslint": "^8.45.0",
"eta": "^2.1.1",
"fs-extra": "^11.1.1",
"inquirer": "^9.2.0",
Expand All @@ -28,5 +31,15 @@
"@types/node": "^18.16.3",
"@types/react": "^18.2.6",
"typescript": "^5.0.4"
},
"eslintConfig": {
"extends": [
"@thoughtbot/eslint-config/base",
"@thoughtbot/eslint-config/prettier",
"@thoughtbot/eslint-config/typescript"
],
"rules": {
"no-console": "off"
}
}
}
18 changes: 10 additions & 8 deletions src/commands/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ 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 { fileURLToPath, URL } from "url";
import addDependency from "../util/addDependency";
import getProjectDir from "../util/getProjectDir";
import getProjectType from "../util/getProjectType";
Expand All @@ -12,24 +12,25 @@ 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));
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"
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 });
await addDependency("typescript @types/react", { dev: true });

const projectType = await getProjectType();
const template = await fs.readFile(
path.join(__dirname, "templates", "tsconfig.json.eta")
path.join(dirname, "templates", "tsconfig.json.eta")
);
const fileContents = eta.render(template.toString(), {
expo: projectType === "expo-bare" || projectType === "expo-managed",
Expand All @@ -40,7 +41,8 @@ export default async function addTypescript() {
});

log(
chalk.green("\n🎉 TypeScript successfully configured") +
"\nConsider renaming your existing JS files as .ts or .tsx.\n"
chalk.green(
"\n🎉 TypeScript successfully configured\nConsider renaming your existing JS files as .ts or .tsx.\n"
)
);
}
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type PackageJson = {
[k: string]: unknown;
dependencies?: {
[k: string]: unknown;
};
devDependencies?: {
[k: string]: unknown;
};
};
4 changes: 2 additions & 2 deletions src/util/addDependency.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { exec } from "child_process";
import * as fs from "fs-extra";
import getProjectDir from "./getProjectDir";
import * as path from "path";
import { exec } from "child_process";
import getProjectDir from "./getProjectDir";

export default async function addDependency(deps: string, { dev = false }) {
const isYarn = await fs.exists(path.join(await getProjectDir(), "yarn.lock"));
Expand Down
1 change: 1 addition & 0 deletions src/util/getProjectDir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default async function getProjectDir(
do {
try {
// This will throw if there is no package.json in the directory
// eslint-disable-next-line no-await-in-loop
await fs.readFile(path.join(dir, "package.json"));

// if didn't throw, package.json exists, return dir
Expand Down
12 changes: 10 additions & 2 deletions src/util/getProjectType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ 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(
const hasExpo = hasProperty(packageJson.dependencies, "expo");
const hasReactNativeUnimodules = hasProperty(
packageJson.dependencies,
"react-native-unimodules"
);
if (hasExpo) {
Expand All @@ -14,3 +15,10 @@ export default async function getProjectType(): Promise<ProjectType> {

return "react-native";
}

function hasProperty(
object: Record<string, unknown> | undefined,
property: string
) {
return Object.prototype.hasOwnProperty.call(object, property);
}
5 changes: 3 additions & 2 deletions src/util/readPackageJson.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import fs from "fs-extra";
import path from "path";
import { PackageJson } from "../types";
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());
return JSON.parse(pkg.toString()) as PackageJson;
}
2 changes: 1 addition & 1 deletion src/util/writeFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default async function writeFile(
{ format = false }: Options = {}
) {
console.log(chalk.bold(`🔨 Creating ${filePath}`));
fs.writeFile(filePath, contents);
await fs.writeFile(filePath, contents);

if (format) {
await formatFile(filePath);
Expand Down
Loading

0 comments on commit 62fb3db

Please sign in to comment.