Skip to content

Commit

Permalink
feat!: support for .jsonc and .yml files (#6)
Browse files Browse the repository at this point in the history
* remove old readme

* add new readme

* add json schema

* edit githooks.ts

* recommend `stackbreak.comment-divider` extension

* fix typo in readme

* update readme

* remove extension

* fix json schema

* fix reader
  • Loading branch information
boywithkeyboard authored Jun 19, 2023
1 parent 87810fb commit a70bc53
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 89 deletions.
49 changes: 0 additions & 49 deletions README.md

This file was deleted.

113 changes: 73 additions & 40 deletions githooks.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,93 @@
export type Githooks = {
githooks: Record<string, string[]>;
};
import {
brightGreen,
brightRed,
gray,
} from "https://deno.land/[email protected]/fmt/colors.ts";
import * as jsonc from "https://deno.land/[email protected]/jsonc/parse.ts";
import * as yaml from "https://deno.land/[email protected]/yaml/parse.ts";

export type GithooksOptions = {
getConfig: () => Promise<Githooks>;
verbose?: boolean;
};
/* find file ---------------------------------------------------------------- */

type ConfigFile =
| `${string}.json`
| `${string}.jsonc`
| `${string}.yaml`
| `${string}.yml`;

export async function readJson(filePath: string): Promise<unknown> {
async function findFile(
...paths: ConfigFile[]
): Promise<
| Record<string, string>
| undefined
> {
try {
const jsonString = await Deno.readTextFile(filePath);
const content = await Deno.readTextFile(paths[0]);

const parsedContent = paths[0].endsWith(".json")
? JSON.parse(content)
: paths[0].endsWith(".jsonc")
? jsonc.parse(content) as Record<string, string>
: yaml.parse(content) as Record<string, string>;

return JSON.parse(jsonString);
} catch (err) {
err.message = `${filePath}: ${err.message}`;
return paths[0].endsWith("deno.json") || paths[0].endsWith("deno.jsonc")
? (parsedContent.githooks ? parsedContent.githooks : undefined)
: parsedContent;
} catch (_) {
paths.shift();

throw err;
if (paths.length > 0) {
return await findFile(...paths);
}
}
}

export function readDenoJson(): Promise<Githooks> {
return readJson("./deno.json") as Promise<Githooks>;
}
/* setup hooks -------------------------------------------------------------- */

const defaultOptions: GithooksOptions = {
getConfig: readDenoJson,
verbose: true,
};
export async function setup({
verbose = true,
file,
}: {
file?: ConfigFile;
verbose?: boolean;
} = {}) {
const githooks = file ? await findFile(file) : await findFile(
"./githooks.json",
"./githooks.jsonc",
"./githooks.yaml",
"./githooks.yml",
"./deno.json",
"./deno.jsonc",
);

export async function setupGithooks(opts: GithooksOptions = defaultOptions) {
const config = await opts.getConfig();
if (!githooks) {
return verbose &&
console.error(brightRed("No githooks found!"));
}

if (!config.githooks) {
opts.verbose &&
console.log("No githooks found in deno.json — skipping setup.");
} else {
const hooks = Object.keys(config.githooks);
const hooks = Object.keys(githooks);

for (const hook of hooks) {
const task = config.githooks[hook];
const hookPath = `./.git/hooks/${hook}`;
const hookScript = `#!/bin/sh\nexec deno task ${task}`;
for (const h of hooks) {
const task = githooks[h];
const hookPath = `./.git/hooks/${h}`;
const hookScript = `#!/bin/sh\nexec deno task ${task}`;

await Deno.writeTextFile(hookPath, hookScript);
await Deno.writeTextFile(hookPath, hookScript);

if (Deno.build.os !== "windows") {
await Deno.chmod(hookPath, 0o755);
}
if (Deno.build.os !== "windows") {
await Deno.chmod(hookPath, 0o755);
}

opts.verbose &&
console.log("Githooks setup successfully:", hooks.join(", "));
}

verbose &&
console.info(
gray(
`${brightGreen("Added githooks:")} ${hooks.join(", ")}`,
),
);
}

// Execute when called directly
/* execute cli -------------------------------------------------------------- */

if (import.meta.main) {
await setupGithooks();
await setup();
}
63 changes: 63 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
## githooks for Deno

This is a simple tool for [Deno](https://deno.land) projects that allows you to
associate specific [deno tasks](https://deno.land/manual/tools/task_runner) with
specific [Git Hooks](https://git-scm.com/docs/githooks) by extending the native
`deno.json` configuration file or adding a separate one.

It works like this:

1. In your `deno.json` (or `deno.jsonc`) file, add a `githooks` key containing a
map of `{githook}` to `{deno task}`. For example:

```json
{
"tasks": {
"start": "deno run -A dev.ts",
"check": "deno fmt --check && deno lint"
},
"githooks": {
"pre-commit": "check"
}
}
```

You can also create a separate `githooks.json`, `githooks.jsonc`,
`githooks.yaml` or `githooks.yml` file:

```json
{
"pre-commit": "check"
}
```

```yaml
pre-commit: check
```
To add autocompletion, you can use our JSON schema. This schema can either be
specified in the settings of your code editor or directly in the JSON file:
```json
{
"$schema": "https://deno.land/x/githooks/schema.json",
"pre-commit": "check"
}
```

2. In your terminal, run the `githooks.ts` script. It will automatically create
a hook file for each githook in your `deno.json` file.

```bash
deno run -A -r https://deno.land/x/githooks/githooks.ts
```

That's it. Now your Git Hook should call `deno task check` before every commit.

---

**PROTIP:** [**deco**](https://github.com/deco-cx/deco) projects come with this
extension pre-installed in the `dev` script. You don't have to do anything, just
add `githooks` to `deno.json` and run `dev` to install the hooks transparently.

---
91 changes: 91 additions & 0 deletions schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
"$id": "https://deno.land/x/githooks/schema.json",
"$schema": "https://json-schema.org/draft-07/schema",
"type": "object",
"properties": {
"applypatch-msg": {
"type": "string"
},
"pre-applypatch": {
"type": "string"
},
"post-applypatch": {
"type": "string"
},
"pre-commit": {
"type": "string"
},
"pre-merge-commit": {
"type": "string"
},
"prepare-commit-msg": {
"type": "string"
},
"commit-msg": {
"type": "string"
},
"post-commit": {
"type": "string"
},
"pre-rebase": {
"type": "string"
},
"post-checkout": {
"type": "string"
},
"post-merge": {
"type": "string"
},
"pre-push": {
"type": "string"
},
"pre-receive": {
"type": "string"
},
"update": {
"type": "string"
},
"proc-receive": {
"type": "string"
},
"post-receive": {
"type": "string"
},
"post-update": {
"type": "string"
},
"reference-transaction": {
"type": "string"
},
"push-to-checkout": {
"type": "string"
},
"pre-auto-gc": {
"type": "string"
},
"post-rewrite": {
"type": "string"
},
"sendemail-validate": {
"type": "string"
},
"fsmonitor-watchman": {
"type": "string"
},
"p4-changelist": {
"type": "string"
},
"p4-prepare-changelist": {
"type": "string"
},
"p4-post-changelist": {
"type": "string"
},
"p4-pre-submit": {
"type": "string"
},
"post-index-change": {
"type": "string"
}
}
}

0 comments on commit a70bc53

Please sign in to comment.