diff --git a/code/check.ts b/code/check.ts index f02ade4..a013bb4 100644 --- a/code/check.ts +++ b/code/check.ts @@ -1,15 +1,38 @@ import { readFileSync, readdirSync, watch } from "node:fs"; import { access } from "node:fs/promises"; import path from "node:path"; +import { type ContributorSchema as ContributorSchemaType } from "../contributors/_schema"; +import { createJiti } from "jiti"; import { parse } from "yaml"; -import { ContributorSchema as ContributorFunction } from "../contributors/_schema"; import z from "zod"; +const jiti = createJiti(import.meta.url); + +async function getContributor() { + // Clear schema cache to pick up changes + Object.keys(jiti.cache).forEach((key) => { + if (key.includes("_schema")) { + delete jiti.cache[key]; + } + }); + const { ContributorSchema } = (await jiti.import( + "../contributors/_schema" + )) as { ContributorSchema: typeof ContributorSchemaType }; + // @ts-expect-error This is fucked up because it's type of image in Astro + return ContributorSchema({ image: z.string }); +} + const TEAM_DIR = path.resolve(process.cwd(), "contributors"); -const PROJECTS_FILE = path.resolve(process.cwd(), "contributors/_schema/projects.ts"); -const SCHEMA_FILE = path.resolve(process.cwd(), "contributors/_schema/index.ts"); +const PROJECTS_FILE = path.resolve( + process.cwd(), + "contributors/_schema/projects.ts" +); +const SCHEMA_FILE = path.resolve( + process.cwd(), + "contributors/_schema/index.ts" +); // Global abort controller to manage running validations let currentValidationController: AbortController | null = null; @@ -21,9 +44,9 @@ interface ValidationResult { } class InvalidRoleError extends Error { - constructor(projectName: string, filePath: string) { + constructor(roleName: string, projectName: string, filePath: string) { super( - `Invalid role in project "${projectName}" for file ./${path.relative( + `Invalid role ${roleName} in project "${projectName}" for file ./${path.relative( process.cwd(), filePath )}.` @@ -42,9 +65,6 @@ class InvalidProjectError extends Error { } } -// @ts-expect-error This is fucked up because it's type of image in Astro -const Contributor = ContributorFunction({ image: z.string}) - function formatValidationError(error: any, filePath: string): string { if (error.code === "unrecognized_keys") { if (error.path.length === 1 && error.path[0].includes("roles")) { @@ -57,7 +77,11 @@ function formatValidationError(error: any, filePath: string): string { const path = error.path.join("."); if (path.includes("roles.")) { const projectName = path.split(".")[1]; - throw new InvalidRoleError(projectName, filePath); + const roleName = error.unionErrors + // TODO: figure out zod typings + .flatMap((e: any) => e.issues) + .find((issue: any) => issue.code == "invalid_enum_value")?.received; + throw new InvalidRoleError(roleName, projectName, filePath); } } @@ -84,6 +108,7 @@ async function validateTeamFile( const content = readFileSync(filePath, "utf-8"); const data = parse(content); + const Contributor = await getContributor(); const validation = await Contributor.superRefine( async (contributor, ctx) => { @@ -149,8 +174,13 @@ function validateAllTeamFiles(signal?: AbortSignal) { return results; } -function printResults(results: ValidationResult[]): void { - console.clear(); +async function printResults( + results: ValidationResult[], + isWatchMode: boolean = false +) { + if (isWatchMode) { + console.clear(); + } const hasErrors = results.some((r) => r.errors.length > 0); if (hasErrors) { @@ -174,6 +204,7 @@ function printResults(results: ValidationResult[]): void { ); if (hasErrors) { + const Contributor = await getContributor(); console.log("\nšŸ’” Tips:"); console.log( ` • Project names and roles must match those in ./${path.relative( @@ -200,7 +231,9 @@ async function runValidation(isWatchMode: boolean = false) { const signal = currentValidationController.signal; try { - console.clear(); + if (isWatchMode) { + console.clear(); + } console.log("Running validation..."); // Needs to wait or it's not clear anything is happening @@ -209,7 +242,7 @@ async function runValidation(isWatchMode: boolean = false) { const results = await Promise.all(await validateAllTeamFiles(signal)); const hasErrors = results.some((r) => !r.isValid); - printResults(await Promise.all(results)); + await printResults(await Promise.all(results)); if (isWatchMode) { console.log("Waiting for more changes..."); } @@ -230,7 +263,7 @@ function startWatchMode(): void { console.log("Time to watch šŸ‘€"); // Run initial validation - runValidation(); + runValidation(true); const watchers: Array<{ close: () => void }> = []; @@ -238,28 +271,25 @@ function startWatchMode(): void { TEAM_DIR, { recursive: true }, (eventType, filename) => { - console.log(`${eventType}: ${filename}`); if ( filename && (filename.endsWith(".yaml") || filename.endsWith(".yml")) ) { - runValidation(); + runValidation(true); } } ); watchers.push(teamWatcher); // Watch projects file - const projectsWatcher = watch(PROJECTS_FILE, (eventType) => { - console.log(`${eventType}: ${PROJECTS_FILE}`); - runValidation(); + const projectsWatcher = watch(PROJECTS_FILE, () => { + runValidation(true); }); watchers.push(projectsWatcher); // Watch schema file - const schemaWatcher = watch(SCHEMA_FILE, (eventType) => { - console.log(`${eventType}: ${SCHEMA_FILE}`); - runValidation(); + const schemaWatcher = watch(SCHEMA_FILE, () => { + runValidation(true); }); watchers.push(schemaWatcher); diff --git a/contributors/_schema/projects.ts b/contributors/_schema/projects.ts index dacd873..ce949e3 100644 --- a/contributors/_schema/projects.ts +++ b/contributors/_schema/projects.ts @@ -8,8 +8,9 @@ export const PROJECTS = [ "Volume 0 Issue 1", "Fandom Cookies", "FujoCoded", + "Learn@", ] as const; -export type Project = typeof PROJECTS[number]; +export type Project = (typeof PROJECTS)[number]; export const VOLUME_0_ISSUE_1_ROLES = [ "Technical Writer", @@ -25,15 +26,9 @@ export const VOLUME_0_ISSUE_1_ROLES = [ "Data Collection & Entry", ]; -export const WEBSITES_ROLES = [ - "fujoweb.dev", - "fujocoded.com", -]; +export const WEBSITES_ROLES = ["fujoweb.dev", "fujocoded.com"]; -export const FUJOCODED_ROLES = [ - "HimeOps", - "FujoCoded", -]; +export const FUJOCODED_ROLES = ["HimeOps", "FujoCoded"]; export const FANDOM_COOKIES_ROLES = [ "Artist", @@ -48,16 +43,19 @@ export const FANDOM_COOKIES_ROLES = [ "Art Direction", "Cookie Catcher Design", "Promo Campaign Assistance", - "Feedback", + "Feedback", ]; -export const PROJECT_ROLES : Record = { +export const LEARN_AT_ROLES = ["Writers Coordinator", "Article Writer"]; + +export const PROJECT_ROLES: Record = { "Volume 0 Issue 1": VOLUME_0_ISSUE_1_ROLES, "Volume 0 Kickstarter": [], "Volume 0": [], - "Websites": WEBSITES_ROLES, + Websites: WEBSITES_ROLES, "Fandom Cookies": FANDOM_COOKIES_ROLES, - "FujoCoded": FUJOCODED_ROLES, + FujoCoded: FUJOCODED_ROLES, + "Learn@": LEARN_AT_ROLES, }; export default PROJECTS; diff --git a/contributors/avatars/rie.png b/contributors/avatars/rie.png new file mode 100644 index 0000000..c72baca Binary files /dev/null and b/contributors/avatars/rie.png differ diff --git a/contributors/rie.yaml b/contributors/rie.yaml new file mode 100644 index 0000000..e86d46e --- /dev/null +++ b/contributors/rie.yaml @@ -0,0 +1,13 @@ +name: Rie +avatar: "./avatars/rie.png" +type: + - community + - contractor +roles: + Learn@: + - role: Writers Coordinator + details: Style Guide Author & Process Coordinator + - role: Article Writer + details: Introduction to NodeJS +contacts: + - https://notavodkashot.carrd.co/ diff --git a/package-lock.json b/package-lock.json index 12858a5..359eb40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@fujocoded/zod-transform-socials": "^0.0.12", + "jiti": "^2.6.1", "zod": "^3.25.76" }, "devDependencies": { @@ -2683,6 +2684,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", diff --git a/package.json b/package.json index 9a1d510..9c1443f 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@fujocoded/zod-transform-socials": "^0.0.12", + "jiti": "^2.6.1", "zod": "^3.25.76" }, "devDependencies": {