Skip to content

Commit

Permalink
Create Prettier install procedure
Browse files Browse the repository at this point in the history
  • Loading branch information
au2001 committed Jan 24, 2024
1 parent 55a6d65 commit e6e8255
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 9 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"build": "rimraf dist && tsc --build",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"format": "prettier --write ."
"format": "prettier --write .",
"format:check": "prettier --check ."
},
"devDependencies": {
"@avicenne-studio/eslint-config": "^1.0.14",
Expand Down
32 changes: 27 additions & 5 deletions src/eslint.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import fs from "fs/promises";

import { exec, fileExists, readJSON, writeJSON } from "./utils.mjs";

const DEV_DEPENDENCIES = ["@avicenne-studio/eslint-config"];
const CONFIG_FILE = ".eslintrc.json";
const JSON_CONFIG = {
extends: "@avicenne-studio",
};
const SCRIPTS = {
lint: "eslint .",
"lint:fix": "eslint --fix .",
};

const getExtraneousPackageConfigTask = async (): Promise<Task | undefined> => {
const manifest = await readJSON("package.json");
Expand Down Expand Up @@ -35,12 +40,10 @@ const getExtraneousConfigsTasks = async (): Promise<Task | undefined> => {
return await getExtraneousPackageConfigTask();
};

const installDevDependenciesTask = async (
...packages: string[]
): Promise<Task | undefined> => {
const installDevDependenciesTask = async (): Promise<Task | undefined> => {
const manifest = await readJSON("package.json");

const missing = packages.filter(
const missing = DEV_DEPENDENCIES.filter(
(packageName) => !(packageName in manifest.devDependencies),
);
if (missing.length === 0) return;
Expand All @@ -49,6 +52,24 @@ const installDevDependenciesTask = async (
return async () => await exec(cmd);
};

const setupScriptsTask = async (): Promise<Task | undefined> => {
const manifest = await readJSON("package.json");

const missing = Object.entries(SCRIPTS).filter(
([script, cmd]) => manifest.scripts?.[script] !== cmd,
);
if (missing.length === 0) return;

return async () => {
await writeJSON(
"package.json",
merge(await readJSON("package.json"), {
scripts: Object.fromEntries(missing),
}),
);
};
};

const updateConfigTask = async (): Promise<Task | undefined> => {
const previousConfig = await readJSON(CONFIG_FILE);

Expand Down Expand Up @@ -76,7 +97,8 @@ export default async () => {
return (
await Promise.all([
getExtraneousConfigsTasks(),
installDevDependenciesTask("@avicenne-studio/eslint-config"),
installDevDependenciesTask(),
setupScriptsTask(),
createConfigTask(),
])
).filter((task): task is Task => task !== undefined);
Expand Down
14 changes: 12 additions & 2 deletions src/index.mts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import inquirer from "inquirer";

import eslint from "./eslint.mjs";
import prettier from "./prettier.mjs";

if (
!process.stdin.isTTY ||
Expand All @@ -16,7 +17,7 @@ if (
if (process.env.npm_config_local_prefix !== undefined)
process.chdir(process.env.npm_config_local_prefix);

const [eslintTasks] = await Promise.all([eslint()]);
const [eslintTasks, prettierTasks] = await Promise.all([eslint(), prettier()]);

const prompts = [];

Expand All @@ -29,7 +30,15 @@ if (eslintTasks.length !== 0) {
});
}

// TODO: Prettier
if (prettierTasks.length !== 0) {
prompts.push({
name: "prettier",
type: "confirm",
message: "Would you like to install Prettier?",
suffix: "",
});
}

// TODO: husky
// TODO: GitHub Actions
// TODO: PR templates
Expand All @@ -40,4 +49,5 @@ const answers = await inquirer.prompt(prompts);

await Promise.all([
...(answers.eslint ? eslintTasks.map(async (task) => await task()) : []),
...(answers.prettier ? prettierTasks.map(async (task) => await task()) : []),
]);
95 changes: 95 additions & 0 deletions src/prettier.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import merge from "deepmerge";
import fs from "fs/promises";

import { exec, fileExists, readJSON, writeJSON } from "./utils.mjs";

const DEV_DEPENDENCIES = ["@avicenne-studio/prettier-config"];
const CONFIG_FILE = ".prettierrc.json";
const JSON_CONFIG = "@avicenne-studio/prettier-config";
const SCRIPTS = {
format: "prettier --write .",
"format:check": "prettier --check .",
};

const getExtraneousPackageConfigTask = async (): Promise<Task | undefined> => {
const manifest = await readJSON("package.json");

if (!("prettier" in manifest)) return;

return () => {
throw new Error(
`Extraneous Prettier config found in package.json. Delete it to continue.`,
);
};
};

const getExtraneousConfigsTasks = async (): Promise<Task | undefined> => {
for (const file of await fs.readdir(".")) {
if (file === CONFIG_FILE || !file.startsWith(".prettierrc")) continue;

return () => {
throw new Error(
`Extraneous Prettier config file found: ${file}. Delete it to continue.`,
);
};
}

return await getExtraneousPackageConfigTask();
};

const installDevDependenciesTask = async (): Promise<Task | undefined> => {
const manifest = await readJSON("package.json");

const missing = DEV_DEPENDENCIES.filter(
(packageName) => !(packageName in manifest.devDependencies),
);
if (missing.length === 0) return;

const cmd = `npm install --save-dev ${missing.map((packageName) => JSON.stringify(packageName)).join(" ")}`;
return async () => await exec(cmd);
};

const setupScriptsTask = async (): Promise<Task | undefined> => {
const manifest = await readJSON("package.json");

const missing = Object.entries(SCRIPTS).filter(
([script, cmd]) => manifest.scripts?.[script] !== cmd,
);
if (missing.length === 0) return;

return async () => {
await writeJSON(
"package.json",
merge(await readJSON("package.json"), {
scripts: Object.fromEntries(missing),
}),
);
};
};

const updateConfigTask = async (): Promise<Task | undefined> => {
const previousConfig = await readJSON(CONFIG_FILE);

if (previousConfig === JSON_CONFIG) return;

return async () => await writeJSON(CONFIG_FILE, JSON_CONFIG);
};

const createConfigTask = async (): Promise<Task | undefined> => {
if (await fileExists(CONFIG_FILE)) return await updateConfigTask();

return async () => {
await writeJSON(CONFIG_FILE, JSON_CONFIG);
};
};

export default async () => {
return (
await Promise.all([
getExtraneousConfigsTasks(),
installDevDependenciesTask(),
setupScriptsTask(),
createConfigTask(),
])
).filter((task): task is Task => task !== undefined);
};
2 changes: 1 addition & 1 deletion src/utils.mts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const readJSON = async (path: string) => {
return JSON.parse(await fs.readFile(path, "utf8"));
};

export const writeJSON = async (path: string, json: object) => {
export const writeJSON = async (path: string, json: unknown) => {
await fs.writeFile(
path,
await prettier.format(JSON.stringify(json, null, " "), {
Expand Down

0 comments on commit e6e8255

Please sign in to comment.