chore(repo): dog-food @websnacksjs/conventional in this repo
This commit is contained in:
parent
99a7717410
commit
a941565528
6 changed files with 177 additions and 5121 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -35,6 +35,10 @@
|
|||
!/flake.nix
|
||||
!/flake.lock
|
||||
|
||||
### Repository configuration ###
|
||||
!/.husky/commit-msg
|
||||
!/conventional.config.js
|
||||
|
||||
### Workspace configuration ###
|
||||
!/package.json
|
||||
!/package-lock.json
|
||||
|
|
|
|||
1
.husky/commit-msg
Normal file
1
.husky/commit-msg
Normal file
|
|
@ -0,0 +1 @@
|
|||
npx conventional commit-msg "$1"
|
||||
125
conventional.config.js
Normal file
125
conventional.config.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import * as fs from "node:fs/promises";
|
||||
import { defineConfig } from "@websnacksjs/conventional";
|
||||
|
||||
/**
|
||||
* @param {import("@websnacksjs/conventional").CommitMessage} message
|
||||
* @returns {void}
|
||||
*/
|
||||
const validateRepoScopedCommit = (message) => {
|
||||
const supportedTypes = ["docs", "chore"];
|
||||
if (!supportedTypes.includes(message.type)) {
|
||||
throw new Error(
|
||||
`${JSON.stringify(message.type)} is not a supported repo-scoped commit type ` +
|
||||
`(must be one of ${JSON.stringify(supportedTypes).replaceAll(",", ", ")})`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const packages = await fs.readdir(new URL("./packages", import.meta.url));
|
||||
const validScopes = ["repo", ...packages];
|
||||
|
||||
/**
|
||||
* @param {import("@websnacksjs/conventional").CommitMessage} message
|
||||
* @returns {void}
|
||||
*/
|
||||
const validatePackageScopedCommit = (message) => {
|
||||
const supportedTypes = ["feat", "fix", "docs", "test", "chore"];
|
||||
if (!supportedTypes.includes(message.type)) {
|
||||
throw new Error(
|
||||
`${JSON.stringify(message.type)} is not a supported package-scoped commit type ` +
|
||||
`(must be one of ${JSON.stringify(supportedTypes).replaceAll(",", ", ")})`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @returns {void}
|
||||
*/
|
||||
const validateUrl = (value) => {
|
||||
try {
|
||||
new URL(value);
|
||||
} catch {
|
||||
const error = new Error(
|
||||
`expected valid URL but got ${JSON.stringify(value)}`,
|
||||
);
|
||||
Error.captureStackTrace(error, validateUrl);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import("@websnacksjs/conventional").Footer[]} footers
|
||||
* @returns void
|
||||
*/
|
||||
const validateFooters = (footers) => {
|
||||
/** @type {string[]} */
|
||||
const unsupportedFooters = [];
|
||||
/** @type {{footer: string, reason: Error}[]} */
|
||||
const invalidFooters = [];
|
||||
for (const { key, value } of footers) {
|
||||
try {
|
||||
switch (key) {
|
||||
case "Merge-request": {
|
||||
validateUrl(value);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
unsupportedFooters.push(key);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (!(error instanceof Error)) {
|
||||
throw new Error(
|
||||
`caught unexpected non-error value ${JSON.stringify(error)}`,
|
||||
);
|
||||
}
|
||||
invalidFooters.push({ footer: key, reason: error });
|
||||
}
|
||||
}
|
||||
|
||||
const errorMessageParts = [];
|
||||
if (unsupportedFooters.length > 0) {
|
||||
let message = `unspported footers in message:`;
|
||||
for (const footer of unsupportedFooters) {
|
||||
message += `\n\t- ${footer}`;
|
||||
}
|
||||
errorMessageParts.push(message);
|
||||
}
|
||||
if (invalidFooters.length > 0) {
|
||||
let message = `invalid footers in message:`;
|
||||
for (const { footer, reason } of invalidFooters) {
|
||||
message += `\n\t- ${footer}: ${reason.message}`;
|
||||
}
|
||||
errorMessageParts.push(message);
|
||||
}
|
||||
if (errorMessageParts.length > 0) {
|
||||
const message = errorMessageParts.join("\n\n");
|
||||
throw new Error(message);
|
||||
}
|
||||
};
|
||||
|
||||
export default defineConfig({
|
||||
validateCommitMessage(message) {
|
||||
if (!message.scope) {
|
||||
throw new Error(
|
||||
`missing required scope (use "repo" for monorepo-related commits or "@websnacksjs/:package" for package-specific commits)`,
|
||||
);
|
||||
}
|
||||
|
||||
if (message.scope === "repo") {
|
||||
validateRepoScopedCommit(message);
|
||||
} else if (packages.includes(message.scope)) {
|
||||
validatePackageScopedCommit(message);
|
||||
} else {
|
||||
throw new Error(
|
||||
[
|
||||
`scope ${JSON.stringify(message.scope)} is unsupported`,
|
||||
`(try one of ${JSON.stringify(validScopes).replace(",", ", ")})`,
|
||||
].join(" "),
|
||||
);
|
||||
}
|
||||
|
||||
validateFooters(message.footers);
|
||||
},
|
||||
});
|
||||
5151
package-lock.json
generated
5151
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,9 +3,11 @@
|
|||
"./packages/*"
|
||||
],
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"clean": "git clean -dxi --exclude .direnv"
|
||||
"clean": "git clean -dxi --exclude .direnv",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "=2.1.3",
|
||||
|
|
@ -13,6 +15,8 @@
|
|||
"@tsconfig/strictest": "^2.0.5",
|
||||
"@types/deno": "^2.3.0",
|
||||
"@types/node": "^24.2.0",
|
||||
"@websnacksjs/conventional": "^0.1.0",
|
||||
"husky": "^9.1.7",
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
{
|
||||
"files": [],
|
||||
"extends": ["./tsconfig.common.json"],
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext",
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
"checkJs": true
|
||||
},
|
||||
"include": ["./*.config.js"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./packages/conventional"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue