@@ -49,10 +49,11 @@ export function parseMdContent(md: string): TutorialFrame | never {
4949 mdContent.summary.description = summaryMatch.groups.tutorialDescription.trim();
5050 }
5151
52+ let current = { level: "0", step: "0" };
5253 // Identify each part of the content
5354 parts.forEach((section: string) => {
5455 // match level
55- const levelRegex = /^(## \s(?<levelId>L\d+)\s(?<levelTitle>.*)[\n\r]*(>\s*(?<levelSummary>.*))?[\n\r]+(?<levelContent>[^]*))/;
56+ const levelRegex = /^(#{2} \s(?<levelId>L\d+)\s(?<levelTitle>.*)[\n\r]*(>\s*(?<levelSummary>.*))?[\n\r]+(?<levelContent>[^]*))/;
5657 const levelMatch: RegExpMatchArray | null = section.match(levelRegex);
5758 if (levelMatch && levelMatch.groups) {
5859 const {
@@ -71,16 +72,33 @@ export function parseMdContent(md: string): TutorialFrame | never {
7172 : truncate(levelContent.trim(), { length: 80, omission: "..." }),
7273 content: levelContent.trim(),
7374 };
75+ current = { level: levelId, step: "0" };
7476 } else {
7577 // match step
76- const stepRegex = /^(### \s(?<stepId>(?<levelId>L\d+)S\d+)\s(?<stepTitle>.*)[\n\r]+(?<stepContent>[^]*))/;
78+ const stepRegex = /^(#{3} \s(?<stepId>(?<levelId>L\d+)S\d+)\s(?<stepTitle>.*)[\n\r]+(?<stepContent>[^]*))/;
7779 const stepMatch: RegExpMatchArray | null = section.match(stepRegex);
7880 if (stepMatch && stepMatch.groups) {
7981 const { stepId, stepContent } = stepMatch.groups;
82+
8083 mdContent.steps[stepId] = {
8184 id: stepId,
8285 content: stepContent.trim(),
8386 };
87+ current = { ...current, step: stepId };
88+ } else {
89+ // parse hints from stepContent
90+ const hintDetectRegex = /^(#{4}\sHINTS[\n\r]+(\*\s(?<hintContent>[^]*))[\n\r]+)+/;
91+ const hintMatch = section.match(hintDetectRegex);
92+ if (!!hintMatch) {
93+ const hintItemRegex = /[\n\r]+\*\s/;
94+ const hints = section
95+ .split(hintItemRegex)
96+ .slice(1) // remove #### HINTS
97+ .map((h) => h.trim());
98+ if (hints.length) {
99+ mdContent.steps[current.step].hints = hints;
100+ }
101+ }
84102 }
85103 }
86104 });
@@ -135,39 +153,45 @@ export function parse(params: ParseParams): any {
135153 }
136154
137155 // add level step commits
138- level.steps = (level.steps || []).map(
139- (step: T.Step, stepIndex: number) => {
140- const stepKey = `${levelSetupKey}S${stepIndex + 1}`;
141- const stepSetupKey = `${stepKey}Q`;
142- if (params.commits[stepSetupKey]) {
143- if (!step.setup) {
144- step.setup = {
145- commits: [],
146- };
156+ try {
157+ level.steps = (level.steps || []).map(
158+ (step: T.Step, stepIndex: number) => {
159+ const stepKey = `${levelSetupKey}S${stepIndex + 1}`;
160+ const stepSetupKey = `${stepKey}Q`;
161+ if (params.commits[stepSetupKey]) {
162+ if (!step.setup) {
163+ step.setup = {
164+ commits: [],
165+ };
166+ }
167+ step.setup.commits = params.commits[stepSetupKey];
147168 }
148- step.setup.commits = params.commits[stepSetupKey];
149- }
150169
151- const stepSolutionKey = `${stepKey}A`;
152- if (params.commits[stepSolutionKey]) {
153- if (!step.solution) {
154- step.solution = {
155- commits: [],
156- };
170+ const stepSolutionKey = `${stepKey}A`;
171+ if (params.commits[stepSolutionKey]) {
172+ if (!step.solution) {
173+ step.solution = {
174+ commits: [],
175+ };
176+ }
177+ step.solution.commits = params.commits[stepSolutionKey];
157178 }
158- step.solution.commits = params.commits[stepSolutionKey];
159- }
160179
161- // add markdown
162- const stepMarkdown: Partial<T.Step> = mdContent.steps[step.id];
163- if (stepMarkdown) {
164- step = { ...step, ...stepMarkdown };
165- }
180+ // add markdown
181+ const stepMarkdown: Partial<T.Step> = mdContent.steps[step.id];
182+ if (stepMarkdown) {
183+ step = { ...step, ...stepMarkdown };
184+ }
166185
167- step.id = `${stepKey}`;
168- return step;
169- }
170- );
186+ step.id = `${stepKey}`;
187+ return step;
188+ }
189+ );
190+ } catch (error) {
191+ console.log(JSON.stringify(level.steps));
192+ console.error("Error parsing level steps");
193+ console.error(error.message);
194+ }
171195
172196 return level;
173197 })