@@ -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+
1019export 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 }
0 commit comments