Skip to content

Commit 2e5cc9b

Browse files
Merge pull request #209 from HackYourFuture-CPH/write-the-goals
feat: write the goals
2 parents 42f499c + c22b6eb commit 2e5cc9b

File tree

5 files changed

+94
-72
lines changed

5 files changed

+94
-72
lines changed

contributing/contributors.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Thanks to these wonderful volunteers and contributors for improving the HackYour
44

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

99
Total: **16** contributors
1010

@@ -20,7 +20,7 @@ Total: **16** contributors
2020
| --- | --- | --- | --- |
2121
| [@MercedesUbeira](https://github.com/MercedesUbeira) | [@shpomp](https://github.com/shpomp) | [@saloumeh-67](https://github.com/saloumeh-67) | [@urbanogilson](https://github.com/urbanogilson) |
2222

23-
<!-- END generateContributors -->
23+
<!-- END generate:contributors -->
2424
<!-- prettier-ignore-end -->
2525

2626
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 👏!

courses/foundation/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ Total: 16 weeks
2222

2323
## Learning goals overview
2424

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

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

@@ -155,3 +157,6 @@ Total: 16 weeks
155157
- [ ] Managing your own tasks within a tight deadline
156158
- [ ] Understanding how and when to ask for help at the right time
157159
- [ ] Taking a project from idea to completion, including deploying it to the web
160+
161+
<!-- END generate:learning-goals -->
162+
<!-- prettier-ignore-end -->

support/src/documentationHelpers/generateContributors.ts

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
* npm run generate:contributors
88
*/
99

10-
import * as fs from "node:fs/promises";
11-
1210
import { Octokit } from "octokit";
11+
import updateFileSection from "./updateFileSection.js";
1312
const listContributors = (o: Octokit) =>
1413
o.request("GET /repos/{owner}/{repo}/contributors");
1514
type Contributor = Awaited<ReturnType<typeof listContributors>>["data"][number];
@@ -152,48 +151,16 @@ Total: **${total}** contributor${total === 1 ? "" : "s"}
152151
return out;
153152
}
154153

155-
async function updateFileSection(
156-
contentToInsert: string,
157-
filename: string,
158-
): Promise<void> {
159-
const oldContent = await fs.readFile(filename, "utf-8");
160-
161-
const matches = [
162-
...oldContent.matchAll(
163-
/(<!-- BEGIN generateContributors -->).*?(<!-- END generateContributors -->)/gs,
164-
),
165-
];
166-
167-
if (matches.length !== 1)
168-
throw new Error(
169-
`Expected to find 1 BEGIN/END pair, found ${matches.length}`,
170-
);
171-
172-
const newContent = [
173-
oldContent.substring(0, matches[0].index),
174-
matches[0][1],
175-
`\n\n${contentToInsert.trim()}\n\n`,
176-
matches[0][2],
177-
oldContent.substring(matches[0].index + matches[0][0].length),
178-
].join("");
179-
180-
if (newContent === oldContent) {
181-
console.log(`${filename} is already up-to-date, no changes made`);
182-
} else {
183-
const tmpFile = filename + ".tmp";
184-
await fs.unlink(tmpFile).catch(() => undefined);
185-
await fs.writeFile(tmpFile, newContent, "utf-8");
186-
await fs.rename(tmpFile, filename);
187-
console.log(`${filename} updated`);
188-
}
189-
}
190-
191154
async function main() {
192155
try {
193156
const raw = await fetchContributors(REPO);
194157
const contributors = filterAndSortContributors(raw);
195158
const md = renderMarkdown(contributors);
196-
await updateFileSection(md, "./contributing/contributors.md");
159+
await updateFileSection(
160+
md,
161+
"./contributing/contributors.md",
162+
"generate:contributors",
163+
);
197164
} catch (err: any) {
198165
console.error("Error:", err.message);
199166
process.exit(1);

support/src/documentationHelpers/generateLearningGoals.ts

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
11
#!/usr/bin/env node
22
/**
3-
* Generate learning goals markdown based on a specific course from programStructure.json.
4-
* The course argument must match the name key in the JSON file exactly.
5-
* It outputs to the command line.
6-
* This markdown is currently used in course Readme.md files.
3+
* Generate learning goals markdown based on a specific course from programStructure.json,
4+
* and update the relevant course's README.md.
75
*
8-
* Note that this code was mostly generated by AI.
6+
* The course argument must match the name key in the JSON file exactly.
97
*
108
* Usage:
119
* npm run generate:learning-goals <course-name>
1210
*/
1311

14-
import { readFile, writeFile, stat } from "fs/promises";
12+
import { readFile, stat } from "fs/promises";
1513
import { join, dirname } from "path";
1614
import { fileURLToPath } from "url";
1715
import process from "process";
16+
import updateFileSection from "./updateFileSection.js";
1817

1918
const scriptDir = dirname(fileURLToPath(import.meta.url));
2019

21-
type ProgramStructure = {
22-
readonly courses: readonly {
20+
type Course = {
21+
readonly name: string;
22+
readonly location: string;
23+
readonly modules: readonly {
2324
readonly name: string;
2425
readonly location: string;
25-
readonly modules: readonly {
26-
readonly name: string;
27-
readonly location: string;
28-
}[];
2926
}[];
3027
};
3128

29+
type ProgramStructure = {
30+
readonly courses: readonly Course[];
31+
};
32+
3233
const jsonPath = "programStructure.json";
33-
const outputLines: string[] = [];
3434

3535
function extractLearningGoals(content: string): {
3636
readonly found: boolean;
@@ -51,16 +51,8 @@ function extractLearningGoals(content: string): {
5151
};
5252
}
5353

54-
async function processCourse(courseName: string): Promise<void> {
55-
const data = JSON.parse(
56-
await readFile(jsonPath, "utf-8"),
57-
) as ProgramStructure;
58-
const course = data.courses.find((c) => c.name === courseName);
59-
60-
if (!course) {
61-
console.log("Course not found in json. Please provide a valid one.");
62-
process.exit(1);
63-
}
54+
async function processCourse(course: Course): Promise<string> {
55+
const outputLines: string[] = [];
6456

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

114-
console.log(outputLines.join("\n"));
106+
return outputLines.join("\n");
115107
}
116108

117109
const courseName = process.argv.slice(2)[0];
@@ -121,11 +113,30 @@ if (!courseName) {
121113
);
122114
process.exit(1);
123115
}
124-
console.log(
125-
"Here's a summary of the learning goals for the course:",
126-
courseName,
116+
117+
const data = JSON.parse(await readFile(jsonPath, "utf-8")) as ProgramStructure;
118+
const course = data.courses.find((c) => c.name === courseName);
119+
120+
if (!course) {
121+
console.log(
122+
"Course not found in json. Please provide one of:",
123+
data.courses.map((c) => c.name).join(", "),
124+
);
125+
process.exit(1);
126+
}
127+
128+
const markdown = await processCourse(course);
129+
130+
const patt = /|/;
131+
const errorsAndWarnings = markdown.split(/\n/).filter((t) => patt.exec(t));
132+
if (errorsAndWarnings.length > 0) {
133+
console.log(errorsAndWarnings.join("\n"));
134+
}
135+
136+
await updateFileSection(
137+
markdown,
138+
`${course.location}/README.md`,
139+
"generate:learning-goals",
127140
);
128-
console.log("You can copy and paste these into the course Readme.md :-)");
129-
console.log("---------------------------------------------------\n");
130141

131-
processCourse(courseName);
142+
if (errorsAndWarnings.length > 0) process.exit(1);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as fs from "node:fs/promises";
2+
3+
export default async function updateFileSection(
4+
contentToInsert: string,
5+
filename: string,
6+
markerName: string,
7+
): Promise<void> {
8+
const oldContent = await fs.readFile(filename, "utf-8");
9+
10+
const pattern = new RegExp(
11+
`(<!-- BEGIN ${markerName} -->).*?(<!-- END ${markerName} -->)`,
12+
"gs",
13+
);
14+
15+
const matches = [...oldContent.matchAll(pattern)];
16+
17+
if (matches.length !== 1)
18+
throw new Error(
19+
`Expected to find 1 BEGIN/END pair, found ${matches.length}`,
20+
);
21+
22+
const newContent = [
23+
oldContent.substring(0, matches[0].index),
24+
matches[0][1],
25+
`\n\n${contentToInsert.trim()}\n\n`,
26+
matches[0][2],
27+
oldContent.substring(matches[0].index + matches[0][0].length),
28+
].join("");
29+
30+
if (newContent === oldContent) {
31+
console.log(`${filename} is already up-to-date, no changes made`);
32+
} else {
33+
const tmpFile = filename + ".tmp";
34+
await fs.unlink(tmpFile).catch(() => undefined);
35+
await fs.writeFile(tmpFile, newContent, "utf-8");
36+
await fs.rename(tmpFile, filename);
37+
console.log(`${filename} updated`);
38+
}
39+
}

0 commit comments

Comments
 (0)