diff --git a/contributing/contributors.md b/contributing/contributors.md index bc1d9f7f..eed921d1 100644 --- a/contributing/contributors.md +++ b/contributing/contributors.md @@ -4,7 +4,7 @@ Thanks to these wonderful volunteers and contributors for improving the HackYour - + Total: **16** contributors @@ -20,7 +20,7 @@ Total: **16** contributors | --- | --- | --- | --- | | [@MercedesUbeira](https://github.com/MercedesUbeira) | [@shpomp](https://github.com/shpomp) | [@saloumeh-67](https://github.com/saloumeh-67) | [@urbanogilson](https://github.com/urbanogilson) | - + There are many people who help test, review and guide the development of our program behind the scenes without committing directly. Let's share a thanks for those people too 👏! diff --git a/courses/foundation/README.md b/courses/foundation/README.md index 0ea522e2..acac09db 100644 --- a/courses/foundation/README.md +++ b/courses/foundation/README.md @@ -22,7 +22,9 @@ Total: 16 weeks ## Learning goals overview - + + + ### [HTML & CSS](/courses/foundation/html-and-css) @@ -155,3 +157,6 @@ Total: 16 weeks - [ ] Managing your own tasks within a tight deadline - [ ] Understanding how and when to ask for help at the right time - [ ] Taking a project from idea to completion, including deploying it to the web + + + diff --git a/support/src/documentationHelpers/generateContributors.ts b/support/src/documentationHelpers/generateContributors.ts index ef5c9971..0e9d0e8e 100644 --- a/support/src/documentationHelpers/generateContributors.ts +++ b/support/src/documentationHelpers/generateContributors.ts @@ -7,9 +7,8 @@ * npm run generate:contributors */ -import * as fs from "node:fs/promises"; - import { Octokit } from "octokit"; +import updateFileSection from "./updateFileSection.js"; const listContributors = (o: Octokit) => o.request("GET /repos/{owner}/{repo}/contributors"); type Contributor = Awaited>["data"][number]; @@ -152,48 +151,16 @@ Total: **${total}** contributor${total === 1 ? "" : "s"} return out; } -async function updateFileSection( - contentToInsert: string, - filename: string, -): Promise { - const oldContent = await fs.readFile(filename, "utf-8"); - - const matches = [ - ...oldContent.matchAll( - /().*?()/gs, - ), - ]; - - if (matches.length !== 1) - throw new Error( - `Expected to find 1 BEGIN/END pair, found ${matches.length}`, - ); - - const newContent = [ - oldContent.substring(0, matches[0].index), - matches[0][1], - `\n\n${contentToInsert.trim()}\n\n`, - matches[0][2], - oldContent.substring(matches[0].index + matches[0][0].length), - ].join(""); - - if (newContent === oldContent) { - console.log(`${filename} is already up-to-date, no changes made`); - } else { - const tmpFile = filename + ".tmp"; - await fs.unlink(tmpFile).catch(() => undefined); - await fs.writeFile(tmpFile, newContent, "utf-8"); - await fs.rename(tmpFile, filename); - console.log(`${filename} updated`); - } -} - async function main() { try { const raw = await fetchContributors(REPO); const contributors = filterAndSortContributors(raw); const md = renderMarkdown(contributors); - await updateFileSection(md, "./contributing/contributors.md"); + await updateFileSection( + md, + "./contributing/contributors.md", + "generate:contributors", + ); } catch (err: any) { console.error("Error:", err.message); process.exit(1); diff --git a/support/src/documentationHelpers/generateLearningGoals.ts b/support/src/documentationHelpers/generateLearningGoals.ts index dec881f4..ee21927b 100644 --- a/support/src/documentationHelpers/generateLearningGoals.ts +++ b/support/src/documentationHelpers/generateLearningGoals.ts @@ -1,36 +1,36 @@ #!/usr/bin/env node /** - * Generate learning goals markdown based on a specific course from programStructure.json. - * The course argument must match the name key in the JSON file exactly. - * It outputs to the command line. - * This markdown is currently used in course Readme.md files. + * Generate learning goals markdown based on a specific course from programStructure.json, + * and update the relevant course's README.md. * - * Note that this code was mostly generated by AI. + * The course argument must match the name key in the JSON file exactly. * * Usage: * npm run generate:learning-goals */ -import { readFile, writeFile, stat } from "fs/promises"; +import { readFile, stat } from "fs/promises"; import { join, dirname } from "path"; import { fileURLToPath } from "url"; import process from "process"; +import updateFileSection from "./updateFileSection.js"; const scriptDir = dirname(fileURLToPath(import.meta.url)); -type ProgramStructure = { - readonly courses: readonly { +type Course = { + readonly name: string; + readonly location: string; + readonly modules: readonly { readonly name: string; readonly location: string; - readonly modules: readonly { - readonly name: string; - readonly location: string; - }[]; }[]; }; +type ProgramStructure = { + readonly courses: readonly Course[]; +}; + const jsonPath = "programStructure.json"; -const outputLines: string[] = []; function extractLearningGoals(content: string): { readonly found: boolean; @@ -51,16 +51,8 @@ function extractLearningGoals(content: string): { }; } -async function processCourse(courseName: string): Promise { - const data = JSON.parse( - await readFile(jsonPath, "utf-8"), - ) as ProgramStructure; - const course = data.courses.find((c) => c.name === courseName); - - if (!course) { - console.log("Course not found in json. Please provide a valid one."); - process.exit(1); - } +async function processCourse(course: Course): Promise { + const outputLines: string[] = []; for (const module of course.modules) { outputLines.push(`### [${module.name}](/${module.location})\n`); @@ -111,7 +103,7 @@ async function processCourse(courseName: string): Promise { outputLines.push(""); } - console.log(outputLines.join("\n")); + return outputLines.join("\n"); } const courseName = process.argv.slice(2)[0]; @@ -121,11 +113,30 @@ if (!courseName) { ); process.exit(1); } -console.log( - "Here's a summary of the learning goals for the course:", - courseName, + +const data = JSON.parse(await readFile(jsonPath, "utf-8")) as ProgramStructure; +const course = data.courses.find((c) => c.name === courseName); + +if (!course) { + console.log( + "Course not found in json. Please provide one of:", + data.courses.map((c) => c.name).join(", "), + ); + process.exit(1); +} + +const markdown = await processCourse(course); + +const patt = /❌|⚠️/; +const errorsAndWarnings = markdown.split(/\n/).filter((t) => patt.exec(t)); +if (errorsAndWarnings.length > 0) { + console.log(errorsAndWarnings.join("\n")); +} + +await updateFileSection( + markdown, + `${course.location}/README.md`, + "generate:learning-goals", ); -console.log("You can copy and paste these into the course Readme.md :-)"); -console.log("---------------------------------------------------\n"); -processCourse(courseName); +if (errorsAndWarnings.length > 0) process.exit(1); diff --git a/support/src/documentationHelpers/updateFileSection.ts b/support/src/documentationHelpers/updateFileSection.ts new file mode 100644 index 00000000..c4032777 --- /dev/null +++ b/support/src/documentationHelpers/updateFileSection.ts @@ -0,0 +1,39 @@ +import * as fs from "node:fs/promises"; + +export default async function updateFileSection( + contentToInsert: string, + filename: string, + markerName: string, +): Promise { + const oldContent = await fs.readFile(filename, "utf-8"); + + const pattern = new RegExp( + `().*?()`, + "gs", + ); + + const matches = [...oldContent.matchAll(pattern)]; + + if (matches.length !== 1) + throw new Error( + `Expected to find 1 BEGIN/END pair, found ${matches.length}`, + ); + + const newContent = [ + oldContent.substring(0, matches[0].index), + matches[0][1], + `\n\n${contentToInsert.trim()}\n\n`, + matches[0][2], + oldContent.substring(matches[0].index + matches[0][0].length), + ].join(""); + + if (newContent === oldContent) { + console.log(`${filename} is already up-to-date, no changes made`); + } else { + const tmpFile = filename + ".tmp"; + await fs.unlink(tmpFile).catch(() => undefined); + await fs.writeFile(tmpFile, newContent, "utf-8"); + await fs.rename(tmpFile, filename); + console.log(`${filename} updated`); + } +}