Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions contributing/contributors.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Thanks to these wonderful volunteers and contributors for improving the HackYour

<!-- To update the contributor list, run "npm run generate:contributors" -->
<!-- prettier-ignore-start -->
<!-- BEGIN generateContributors -->
<!-- BEGIN generate:contributors -->

Total: **16** contributors

Expand All @@ -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) |

<!-- END generateContributors -->
<!-- END generate:contributors -->
<!-- prettier-ignore-end -->

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 👏!
Expand Down
7 changes: 6 additions & 1 deletion courses/foundation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ Total: 16 weeks

## Learning goals overview

<!-- This summary can be automatically generated by running "npm run generate:learning-goals and pasted here -->
<!-- To update this section, use "npm run generate:learning-goals Foundation" -->
<!-- prettier-ignore-start -->
<!-- BEGIN generate:learning-goals -->

### [HTML & CSS](/courses/foundation/html-and-css)

Expand Down Expand Up @@ -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

<!-- END generate:learning-goals -->
<!-- prettier-ignore-end -->
45 changes: 6 additions & 39 deletions support/src/documentationHelpers/generateContributors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ReturnType<typeof listContributors>>["data"][number];
Expand Down Expand Up @@ -152,48 +151,16 @@ Total: **${total}** contributor${total === 1 ? "" : "s"}
return out;
}

async function updateFileSection(
contentToInsert: string,
filename: string,
): Promise<void> {
const oldContent = await fs.readFile(filename, "utf-8");

const matches = [
...oldContent.matchAll(
/(<!-- BEGIN generateContributors -->).*?(<!-- END generateContributors -->)/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);
Expand Down
71 changes: 41 additions & 30 deletions support/src/documentationHelpers/generateLearningGoals.ts
Original file line number Diff line number Diff line change
@@ -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 <course-name>
*/

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;
Expand All @@ -51,16 +51,8 @@ function extractLearningGoals(content: string): {
};
}

async function processCourse(courseName: string): Promise<void> {
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<string> {
const outputLines: string[] = [];

for (const module of course.modules) {
outputLines.push(`### [${module.name}](/${module.location})\n`);
Expand Down Expand Up @@ -111,7 +103,7 @@ async function processCourse(courseName: string): Promise<void> {
outputLines.push("");
}

console.log(outputLines.join("\n"));
return outputLines.join("\n");
}

const courseName = process.argv.slice(2)[0];
Expand All @@ -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);
39 changes: 39 additions & 0 deletions support/src/documentationHelpers/updateFileSection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as fs from "node:fs/promises";

export default async function updateFileSection(
contentToInsert: string,
filename: string,
markerName: string,
): Promise<void> {
const oldContent = await fs.readFile(filename, "utf-8");

const pattern = new RegExp(
`(<!-- BEGIN ${markerName} -->).*?(<!-- END ${markerName} -->)`,
"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`);
}
}