@@ -7,6 +7,15 @@ type TutorialFrame = {
77levels :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 } \s H I N T S [ \n \r ] + ( [ \* | \- ] \s (?< hintContent > [ ^ ] * ) ) [ \n \r ] + ) + / ,
15+ subtasks :/ ^ ( # { 4 } \s S U B T A S K S [ \n \r ] + ( [ \* | \- ] \s (?< subtaskContent > [ ^ ] * ) ) [ \n \r ] + ) + / ,
16+ listItem :/ [ \n \r ] + [ \* | \- ] \s / ,
17+ } ;
18+
1019export function parseMdContent ( md :string ) :TutorialFrame | never {
1120let start :number = - 1 ;
1221const 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 ) ;
4047if ( summaryMatch . groups . tutorialTitle ) {
4148mdContent . 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
5057parts . 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
5561if ( levelMatch && levelMatch . groups ) {
5662const 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 ) ;
8287if ( stepMatch && stepMatch . groups ) {
8388current = {
8489levelId :current . levelId ,
@@ -91,17 +96,14 @@ export function parseMdContent(md: string): TutorialFrame | never {
9196content :stepContent . trim ( ) ,
9297} ;
9398} else {
94- const hintDetectRegex = / ^ ( # { 4 } \s H I N T S [ \n \r ] + ( [ \* | \- ] \s (?< hintContent > [ ^ ] * ) ) [ \n \r ] + ) + / ;
95- const hintMatch = section . match ( hintDetectRegex ) ;
96- const subtaskDetectRegex = / ^ ( # { 4 } \s S U B T A S K S [ \n \r ] + ( [ \* | \- ] \s (?< subtaskContent > [ ^ ] * ) ) [ \n \r ] + ) + / ;
97- const subtaskMatch = section . match ( subtaskDetectRegex ) ;
98- const listItemregex = / [ \n \r ] + [ \* | \- ] \s / ;
99+ const hintMatch = section . match ( R . hints ) ;
100+ const subtaskMatch = section . match ( R . subtasks ) ;
99101
100102switch ( true ) {
101103// parse hints from stepContent
102104case ! ! hintMatch :
103105const hints = section
104- . split ( listItemregex )
106+ . split ( R . listItem )
105107. slice ( 1 ) // remove #### HINTS
106108. map ( ( h ) => h . trim ( ) ) ;
107109if ( hints . length ) {
@@ -113,7 +115,7 @@ export function parseMdContent(md: string): TutorialFrame | never {
113115// parse subtasks from stepContent
114116case ! ! subtaskMatch :
115117const subtasks = section
116- . split ( listItemregex )
118+ . split ( R . listItem )
117119. slice ( 1 ) // remove #### SUBTASKS
118120. map ( ( h ) => h . trim ( ) ) ;
119121if ( subtasks . length ) {