-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #310 from nlibjs/refactor
Add some docs and a test for disabling hooks
- Loading branch information
Showing
16 changed files
with
286 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,16 @@ | ||
#!/usr/bin/env node | ||
import * as process from "node:process"; | ||
import { usage } from "./config.mjs"; | ||
import { disable } from "./disable.mjs"; | ||
import { enable } from "./enable.mjs"; | ||
import { spawnSync } from "./spawnSync.mjs"; | ||
|
||
switch (process.argv[2]) { | ||
case "enable": | ||
await enable({ hooksDirectory: ".githooks" }); | ||
await enable(); | ||
break; | ||
case "disable": | ||
spawnSync("git config --local --unset core.hooksPath"); | ||
await disable(); | ||
break; | ||
default: | ||
throw new Error(`UnexpectedAction: ${process.argv.slice(2).join(" ")}`); | ||
console.info(usage); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export const packageName = "@nlib/githooks"; | ||
export const dirnameForHooks = ".githooks"; | ||
export const usage = ` | ||
Usage: | ||
npx ${packageName} enable | ||
Enables the hooks | ||
npx ${packageName} disable | ||
Disables the hooks | ||
`.trim(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import * as fs from "node:fs"; | ||
import { dirnameForHooks, packageName } from "./config.mjs"; | ||
import { getDirectories } from "./getDirectories.mjs"; | ||
import { run } from "./run.mjs"; | ||
import { statOrNull } from "./statOrNull.mjs"; | ||
|
||
export const disable = async () => { | ||
const dirs = getDirectories(); | ||
await run("git config --local --unset core.hooksPath", dirs.projectRoot); | ||
await run(`npm uninstall ${packageName}`, dirs.projectRoot); | ||
const stats = await statOrNull(dirs.hooks); | ||
if (stats) { | ||
if (stats.isDirectory() && fs.readdirSync(dirs.hooks).length === 0) { | ||
fs.rmdirSync(dirs.hooks); | ||
} else { | ||
console.info(`${dirnameForHooks} is not empty.`); | ||
console.info(`Please remove the ${dirnameForHooks} directory manually.`); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,15 @@ | ||
import * as console from "node:console"; | ||
import * as fs from "node:fs/promises"; | ||
import * as path from "node:path"; | ||
import { dirnameForHooks, packageName } from "./config.mjs"; | ||
import { getDirectories } from "./getDirectories.mjs"; | ||
import { isDirectDependency } from "./isDirectDependency.mjs"; | ||
import { spawnSync } from "./spawnSync.mjs"; | ||
import { run } from "./run.mjs"; | ||
|
||
const getPackageName = async () => { | ||
const jsonUrl = new URL("../package.json", import.meta.url); | ||
const json = await fs.readFile(jsonUrl, "utf8"); | ||
const { name } = JSON.parse(json); | ||
if (typeof name !== "string") { | ||
throw new TypeError("Cannot read package name from package.json"); | ||
export const enable = async () => { | ||
if (isDirectDependency(packageName)) { | ||
const dirs = getDirectories(); | ||
await fs.mkdir(dirs.hooks, { recursive: true }); | ||
console.info(`${packageName}: created ${dirs.hooks}`); | ||
const command = `git config --local core.hooksPath ${dirnameForHooks}`; | ||
await run(command, dirs.projectRoot); | ||
} | ||
return name; | ||
}; | ||
|
||
export const enable = async ({ | ||
hooksDirectory, | ||
}: { hooksDirectory: string }) => { | ||
const packageName = await getPackageName(); | ||
if (!isDirectDependency(packageName)) { | ||
return; | ||
} | ||
const { stdout: projectRoot } = spawnSync("git rev-parse --show-toplevel"); | ||
console.info(`${packageName}.enable: mkdir -p ${projectRoot}`); | ||
await fs.mkdir(path.join(projectRoot, hooksDirectory), { recursive: true }); | ||
spawnSync(`git config --local core.hooksPath ${hooksDirectory}`); | ||
console.info(`${packageName}.enable: done`); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { execSync } from "node:child_process"; | ||
import * as path from "node:path"; | ||
import { dirnameForHooks } from "./config.mjs"; | ||
import { memoize } from "./memoize.mjs"; | ||
|
||
export const getDirectories = memoize(() => { | ||
const command = "git rev-parse --show-toplevel"; | ||
const cwd = new URL("..", import.meta.url); | ||
const projectRoot = `${execSync(command, { cwd })}`.trim(); | ||
const hooks = path.join(projectRoot, dirnameForHooks); | ||
return { projectRoot, hooks }; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,24 @@ | ||
import { spawnSync } from "./spawnSync.mjs"; | ||
import { execSync } from "node:child_process"; | ||
import { getDirectories } from "./getDirectories.mjs"; | ||
import { isObjectLike } from "./isObjectLike.mjs"; | ||
import { memoize } from "./memoize.mjs"; | ||
|
||
interface NpmLsOutput { | ||
name?: string; | ||
dependencies?: Record<string, unknown>; | ||
} | ||
|
||
const listDirectDependencies = function* (): Generator<string> { | ||
const { stdout } = spawnSync("npm ls --depth=0 --json"); | ||
const { name = "", dependencies = {} } = JSON.parse( | ||
`${stdout}`.trim(), | ||
) as NpmLsOutput; | ||
if (name) { | ||
yield name; | ||
} | ||
for (const key of Object.keys(dependencies)) { | ||
yield key; | ||
} | ||
}; | ||
|
||
let cached: Set<string> | undefined; | ||
export const getDirectDependencies = (): Set<string> => { | ||
if (!cached) { | ||
cached = new Set(listDirectDependencies()); | ||
const getDirectDependencies = memoize(() => { | ||
const dirs = getDirectories(); | ||
const parseResult = JSON.parse( | ||
`${execSync("npm ls --depth=0 --json", { cwd: dirs.projectRoot })}`, | ||
); | ||
if (isObjectLike(parseResult) && isObjectLike(parseResult.dependencies)) { | ||
return new Set(Object.keys(parseResult.dependencies)); | ||
} | ||
return cached; | ||
}; | ||
throw new Error("Failed to get dependencies."); | ||
}); | ||
|
||
/** | ||
* Check if a package is a direct dependency of the current project. | ||
* A direct dependency is a package that is listed in the package.json file. | ||
* This function gets the list of direct dependencies by executing | ||
* `npm ls --depth=0 --json`. | ||
*/ | ||
export const isDirectDependency = (packageName: string): boolean => | ||
getDirectDependencies().has(packageName); |
Oops, something went wrong.