Skip to content

Commit 3c214e7

Browse files
authored
Merge pull request #57 from coderoad/feature/subtasks
Feature/subtasks
2 parents 71e9c79 + 859d8e5 commit 3c214e7

File tree

3 files changed

+115
-31
lines changed

3 files changed

+115
-31
lines changed

Diff for: src/utils/parse.ts

+42-21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ type TutorialFrame = {
77
levels: T.Level[];
88
};
99

10+
const R = {
11+
summary: /^#\s(?<tutorialTitle>.*)[\n\r]+(?<tutorialDescription>[^]*)/,
12+
level: /^(#{2}\s(?<levelId>L?\d+\.?)\s(?<levelTitle>.*)[\n\r]*(>\s(?<levelSummary>.*))?[\n\r]+(?<levelContent>[^]*))/,
13+
step: /^(#{3}\s(?<stepTitle>.*)[\n\r]+(?<stepContent>[^]*))/,
14+
hints: /^(#{4}\sHINTS[\n\r]+([\*|\-]\s(?<hintContent>[^]*))[\n\r]+)+/,
15+
subtasks: /^(#{4}\sSUBTASKS[\n\r]+([\*|\-]\s(?<subtaskContent>[^]*))[\n\r]+)+/,
16+
listItem: /[\n\r]+[\*|\-]\s/,
17+
};
18+
1019
export function parseMdContent(md: string): TutorialFrame | never {
1120
let start: number = -1;
1221
const parts: any[] = [];
@@ -34,9 +43,7 @@ export function parseMdContent(md: string): TutorialFrame | never {
3443
};
3544

3645
// Capture summary
37-
const summaryMatch = parts
38-
.shift()
39-
.match(/^#\s(?<tutorialTitle>.*)[\n\r]+(?<tutorialDescription>[^]*)/);
46+
const summaryMatch = parts.shift().match(R.summary);
4047
if (summaryMatch.groups.tutorialTitle) {
4148
mdContent.summary.title = summaryMatch.groups.tutorialTitle.trim();
4249
}
@@ -49,8 +56,7 @@ export function parseMdContent(md: string): TutorialFrame | never {
4956
// Identify each part of the content
5057
parts.forEach((section: string) => {
5158
// match level
52-
const levelRegex = /^(#{2}\s(?<levelId>L?\d+\.?)\s(?<levelTitle>.*)[\n\r]*(>\s(?<levelSummary>.*))?[\n\r]+(?<levelContent>[^]*))/;
53-
const levelMatch: RegExpMatchArray | null = section.match(levelRegex);
59+
const levelMatch: RegExpMatchArray | null = section.match(R.level);
5460

5561
if (levelMatch && levelMatch.groups) {
5662
const levelId = levelMatch.groups.levelId.replace(".", "");
@@ -77,8 +83,7 @@ export function parseMdContent(md: string): TutorialFrame | never {
7783
};
7884
} else {
7985
// match step
80-
const stepRegex = /^(#{3}\s(?<stepTitle>.*)[\n\r]+(?<stepContent>[^]*))/;
81-
const stepMatch: RegExpMatchArray | null = section.match(stepRegex);
86+
const stepMatch: RegExpMatchArray | null = section.match(R.step);
8287
if (stepMatch && stepMatch.groups) {
8388
current = {
8489
levelId: current.levelId,
@@ -91,20 +96,36 @@ export function parseMdContent(md: string): TutorialFrame | never {
9196
content: stepContent.trim(),
9297
};
9398
} else {
94-
// parse hints from stepContent
95-
const hintDetectRegex = /^(#{4}\sHINTS[\n\r]+([\*|\-]\s(?<hintContent>[^]*))[\n\r]+)+/;
96-
const hintMatch = section.match(hintDetectRegex);
97-
if (!!hintMatch) {
98-
const hintItemRegex = /[\n\r]+[\*|\-]\s/;
99-
const hints = section
100-
.split(hintItemRegex)
101-
.slice(1) // remove #### HINTS
102-
.map((h) => h.trim());
103-
if (hints.length) {
104-
mdContent.levels[current.levelIndex].steps[
105-
current.stepIndex
106-
].hints = hints;
107-
}
99+
const hintMatch = section.match(R.hints);
100+
const subtaskMatch = section.match(R.subtasks);
101+
102+
switch (true) {
103+
// parse hints from stepContent
104+
case !!hintMatch:
105+
const hints = section
106+
.split(R.listItem)
107+
.slice(1) // remove #### HINTS
108+
.map((h) => h.trim());
109+
if (hints.length) {
110+
mdContent.levels[current.levelIndex].steps[
111+
current.stepIndex
112+
].hints = hints;
113+
}
114+
return;
115+
// parse subtasks from stepContent
116+
case !!subtaskMatch:
117+
const subtasks = section
118+
.split(R.listItem)
119+
.slice(1) // remove #### SUBTASKS
120+
.map((h) => h.trim());
121+
if (subtasks.length) {
122+
mdContent.levels[current.levelIndex].steps[
123+
current.stepIndex
124+
].subtasks = subtasks;
125+
}
126+
return;
127+
default:
128+
console.warn(`No build parser match found for:\n${section}\n`);
108129
}
109130
}
110131
}

Diff for: tests/parse.test.ts

+71-8
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,6 @@ The first step
541541
files: ["someFile.js"],
542542
watchers: ["someFile.js"],
543543
filter: "someFilter",
544-
subtasks: true,
545544
},
546545
solution: {
547546
commands: ["npm install"],
@@ -579,7 +578,6 @@ The first step
579578
files: ["someFile.js"],
580579
watchers: ["someFile.js"],
581580
filter: "someFilter",
582-
subtasks: true,
583581
},
584582
solution: {
585583
commits: ["1gfedcba", "987654321"],
@@ -631,7 +629,6 @@ The third step
631629
files: ["someFile.js"],
632630
watchers: ["someFile.js"],
633631
filter: "someFilter",
634-
subtasks: true,
635632
},
636633
solution: {
637634
commands: ["npm install"],
@@ -645,7 +642,6 @@ The third step
645642
files: ["someFile.js"],
646643
watchers: ["someFile.js"],
647644
filter: "someFilter",
648-
subtasks: true,
649645
},
650646
solution: {
651647
commands: ["npm install"],
@@ -666,7 +662,6 @@ The third step
666662
files: ["someFile.js"],
667663
watchers: ["someFile.js"],
668664
filter: "someFilter",
669-
subtasks: true,
670665
},
671666
solution: {
672667
commands: ["npm install"],
@@ -709,7 +704,6 @@ The third step
709704
files: ["someFile.js"],
710705
watchers: ["someFile.js"],
711706
filter: "someFilter",
712-
subtasks: true,
713707
},
714708
solution: {
715709
commits: ["1fedcba", "987654321"],
@@ -726,7 +720,6 @@ The third step
726720
files: ["someFile.js"],
727721
watchers: ["someFile.js"],
728722
filter: "someFilter",
729-
subtasks: true,
730723
},
731724
solution: {
732725
commits: ["3abcdef"],
@@ -751,7 +744,6 @@ The third step
751744
files: ["someFile.js"],
752745
watchers: ["someFile.js"],
753746
filter: "someFilter",
754-
subtasks: true,
755747
},
756748
solution: {
757749
commits: ["5abcdef"],
@@ -1419,4 +1411,75 @@ The second uninterrupted step
14191411
expect(result.levels[0]).toEqual(expected.levels[0]);
14201412
});
14211413
});
1414+
describe("subtasks", () => {
1415+
it("should parse subtasks", () => {
1416+
const md = `# Subtask Demo
1417+
1418+
A demo demonstrating how to use subtasks
1419+
1420+
## 1. Subtask Example
1421+
1422+
A subtask example
1423+
1424+
### 1.1
1425+
1426+
Create a function \`add\` that can take a variety of params.
1427+
1428+
#### SUBTASKS
1429+
1430+
- Add one number
1431+
- Add two numbers
1432+
- Add three numbers
1433+
`;
1434+
const skeleton = {
1435+
levels: [
1436+
{
1437+
id: "1",
1438+
steps: [
1439+
{
1440+
id: "1.1",
1441+
},
1442+
],
1443+
},
1444+
],
1445+
};
1446+
const expected = {
1447+
levels: [
1448+
{
1449+
id: "1",
1450+
title: "Subtask Example",
1451+
summary: "A subtask example",
1452+
content: "A subtask example",
1453+
steps: [
1454+
{
1455+
id: "1.1",
1456+
setup: {
1457+
commits: ["abcdef1"],
1458+
},
1459+
content:
1460+
"Create a function `add` that can take a variety of params.",
1461+
solution: {
1462+
commits: ["abcdef2"],
1463+
},
1464+
subtasks: [
1465+
"Add one number",
1466+
"Add two numbers",
1467+
"Add three numbers",
1468+
],
1469+
},
1470+
],
1471+
},
1472+
],
1473+
};
1474+
const result = parse({
1475+
text: md,
1476+
skeleton,
1477+
commits: {
1478+
"1.1:T": ["abcdef1"],
1479+
"1.1:S": ["abcdef2"],
1480+
},
1481+
});
1482+
expect(result.levels[0]).toEqual(expected.levels[0]);
1483+
});
1484+
});
14221485
});

Diff for: typings/tutorial.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export type Step = {
2727
content: string;
2828
setup?: StepActions;
2929
solution?: Maybe<StepActions>;
30-
subtasks?: { [testName: string]: boolean };
30+
subtasks?: string[];
3131
hints?: string[];
3232
};
3333

@@ -52,7 +52,7 @@ export type StepActions = {
5252
files?: string[];
5353
watchers?: string[];
5454
filter?: string;
55-
subtasks?: boolean;
55+
subtasks?: string[];
5656
};
5757

5858
export interface TestRunnerArgs {

0 commit comments

Comments
 (0)