@@ -4,10 +4,7 @@ import * as T from "../../typings/tutorial";
4
4
5
5
type TutorialFrame = {
6
6
summary : T . TutorialSummary ;
7
- levels : {
8
- [ levelKey : string ] : T . Level ;
9
- } ;
10
- steps : { [ stepKey : string ] : Partial < T . Step > } ;
7
+ levels : T . Level [ ] ;
11
8
} ;
12
9
13
10
export function parseMdContent ( md : string ) : TutorialFrame | never {
@@ -33,8 +30,7 @@ export function parseMdContent(md: string): TutorialFrame | never {
33
30
title : "" ,
34
31
description : "" ,
35
32
} ,
36
- levels : { } ,
37
- steps : { } ,
33
+ levels : [ ] ,
38
34
} ;
39
35
40
36
// Capture summary
@@ -49,23 +45,20 @@ export function parseMdContent(md: string): TutorialFrame | never {
49
45
mdContent . summary . description = summaryMatch . groups . tutorialDescription . trim ( ) ;
50
46
}
51
47
52
- let current = { level : "0" , step : "0" } ;
48
+ let current = { level : - 1 , step : - 1 } ;
53
49
// Identify each part of the content
54
50
parts . forEach ( ( section : string ) => {
55
51
// match level
56
- const levelRegex = / ^ ( # { 2 } \s (?< levelId > L \d + ) \s (?< levelTitle > .* ) [ \n \r ] * ( > \s (?< levelSummary > .* ) ) ? [ \n \r ] + (?< levelContent > [ ^ ] * ) ) / ;
52
+ const levelRegex = / ^ ( # { 2 } \s (?< levelId > L ? \d + \. ? ) \s (?< levelTitle > .* ) [ \n \r ] * ( > \s (?< levelSummary > .* ) ) ? [ \n \r ] + (?< levelContent > [ ^ ] * ) ) / ;
57
53
const levelMatch : RegExpMatchArray | null = section . match ( levelRegex ) ;
54
+
58
55
if ( levelMatch && levelMatch . groups ) {
59
- const {
60
- levelId,
61
- levelTitle,
62
- levelSummary,
63
- levelContent,
64
- } = levelMatch . groups ;
56
+ current = { level : current . level + 1 , step : - 1 } ;
57
+ const { levelTitle, levelSummary, levelContent } = levelMatch . groups ;
65
58
66
59
// @ts -ignore
67
- mdContent . levels [ levelId ] = {
68
- id : levelId ,
60
+ mdContent . levels [ current . level ] = {
61
+ id : ( current . level + 1 ) . toString ( ) ,
69
62
title : levelTitle . trim ( ) ,
70
63
summary :
71
64
levelSummary && levelSummary . trim ( ) . length
@@ -75,23 +68,22 @@ export function parseMdContent(md: string): TutorialFrame | never {
75
68
omission : "..." ,
76
69
} ) ,
77
70
content : levelContent . trim ( ) ,
71
+ steps : [ ] ,
78
72
} ;
79
- current = { level : levelId , step : "0" } ;
80
73
} else {
81
74
// match step
82
- const stepRegex = / ^ ( # { 3 } \s (?< stepId > (?< levelId > L \d + ) S \d + ) \s (?< stepTitle > .* ) [ \n \r ] + (?< stepContent > [ ^ ] * ) ) / ;
75
+ const stepRegex = / ^ ( # { 3 } \s (?< stepTitle > .* ) [ \n \r ] + (?< stepContent > [ ^ ] * ) ) / ;
83
76
const stepMatch : RegExpMatchArray | null = section . match ( stepRegex ) ;
84
77
if ( stepMatch && stepMatch . groups ) {
78
+ current = { level : current . level , step : current . step + 1 } ;
85
79
const { stepId, stepContent } = stepMatch . groups ;
86
-
87
- mdContent . steps [ stepId ] = {
88
- id : stepId ,
80
+ mdContent . levels [ current . level ] . steps [ current . step ] = {
81
+ id : `${ current . level + 1 } .${ current . step + 1 } ` ,
89
82
content : stepContent . trim ( ) ,
90
83
} ;
91
- current = { ...current , step : stepId } ;
92
84
} else {
93
85
// parse hints from stepContent
94
- const hintDetectRegex = / ^ ( # { 4 } \s H I N T S [ \n \r ] + ( \* \s (?< hintContent > [ ^ ] * ) ) [ \n \r ] + ) + / ;
86
+ const hintDetectRegex = / ^ ( # { 4 } \s H I N T S [ \n \r ] + ( [ \* | \- ] \s (?< hintContent > [ ^ ] * ) ) [ \n \r ] + ) + / ;
95
87
const hintMatch = section . match ( hintDetectRegex ) ;
96
88
if ( ! ! hintMatch ) {
97
89
const hintItemRegex = / [ \n \r ] + \* \s / ;
@@ -100,7 +92,7 @@ export function parseMdContent(md: string): TutorialFrame | never {
100
92
. slice ( 1 ) // remove #### HINTS
101
93
. map ( ( h ) => h . trim ( ) ) ;
102
94
if ( hints . length ) {
103
- mdContent . steps [ current . step ] . hints = hints ;
95
+ mdContent . levels [ current . level ] . steps [ current . step ] . hints = hints ;
104
96
}
105
97
}
106
98
}
@@ -135,33 +127,30 @@ export function parse(params: ParseParams): any {
135
127
} ;
136
128
}
137
129
138
- // merge content and tutorial
139
- if ( params . skeleton . levels && params . skeleton . levels . length ) {
140
- parsed . levels = params . skeleton . levels
141
- . map ( ( level : T . Level , levelIndex : number ) => {
142
- const levelContent = mdContent . levels [ level . id ] ;
130
+ // merge content levels and tutorial
143
131
144
- if ( ! levelContent ) {
145
- return null ;
146
- }
132
+ parsed . levels = mdContent . levels . map (
133
+ ( mdLevel : T . Level , mdLevelIndex : number ) => {
134
+ // add level setup commits
135
+ let level : T . Level = { ...mdLevel } ;
147
136
148
- level = { ...level , ...levelContent } ;
149
-
150
- // add level setup commits
151
- const levelSetupKey = level . id ;
152
- if ( params . commits [ levelSetupKey ] ) {
153
- level . setup = {
154
- ...( level . setup || { } ) ,
155
- commits : params . commits [ levelSetupKey ] ,
156
- } ;
157
- }
137
+ const configLevel = params . skeleton . levels [ mdLevelIndex ] ;
158
138
139
+ if ( configLevel ) {
159
140
// add level step commits
160
- try {
161
- level . steps = ( level . steps || [ ] ) . map (
162
- ( step : T . Step , stepIndex : number ) => {
163
- const stepKey = `${ levelSetupKey } S${ stepIndex + 1 } ` ;
164
- const stepSetupKey = `${ stepKey } Q` ;
141
+ const { steps, ...configLevelProps } = configLevel ;
142
+ level = { ...configLevelProps , ...level } ;
143
+ if ( steps ) {
144
+ steps . forEach ( ( step : T . Step , index : number ) => {
145
+ try {
146
+ const mdStep = level . steps [ index ] ;
147
+
148
+ step = {
149
+ ...step ,
150
+ ...mdStep ,
151
+ } ;
152
+
153
+ const stepSetupKey = `${ step . id } :T` ;
165
154
if ( params . commits [ stepSetupKey ] ) {
166
155
if ( ! step . setup ) {
167
156
step . setup = {
@@ -171,7 +160,7 @@ export function parse(params: ParseParams): any {
171
160
step . setup . commits = params . commits [ stepSetupKey ] ;
172
161
}
173
162
174
- const stepSolutionKey = `${ stepKey } A ` ;
163
+ const stepSolutionKey = `${ step . id } :S ` ;
175
164
if ( params . commits [ stepSolutionKey ] ) {
176
165
if ( ! step . solution ) {
177
166
step . solution = {
@@ -180,27 +169,35 @@ export function parse(params: ParseParams): any {
180
169
}
181
170
step . solution . commits = params . commits [ stepSolutionKey ] ;
182
171
}
172
+ } catch ( error ) {
173
+ console . error ( "Error parsing level steps" ) ;
174
+ console . warn ( JSON . stringify ( level . steps ) ) ;
175
+ console . error ( error . message ) ;
176
+ }
177
+ // update level step
178
+ level . steps [ index ] = step ;
179
+ } ) ;
180
+ }
181
+ }
183
182
184
- // add markdown
185
- const stepMarkdown : Partial < T . Step > = mdContent . steps [ step . id ] ;
186
- if ( stepMarkdown ) {
187
- step = { ...step , ...stepMarkdown } ;
188
- }
183
+ if ( params . commits [ level . id ] ) {
184
+ if ( ! level . setup ) {
185
+ level . setup = { } ;
186
+ }
187
+ level . setup . commits = params . commits [ level . id ] ;
188
+ }
189
189
190
- step . id = `${ stepKey } ` ;
191
- return step ;
192
- }
193
- ) ;
194
- } catch ( error ) {
195
- console . log ( JSON . stringify ( level . steps ) ) ;
196
- console . error ( "Error parsing level steps" ) ;
197
- console . error ( error . message ) ;
190
+ // @deprecated L1 system
191
+ if ( params . commits [ `L${ level . id } ` ] ) {
192
+ if ( ! level . setup ) {
193
+ level . setup = { } ;
198
194
}
195
+ level . setup . commits = params . commits [ `L${ level . id } ` ] ;
196
+ }
199
197
200
- return level ;
201
- } )
202
- . filter ( ( l : T . Level | null ) => ! ! l ) ;
203
- }
198
+ return level ;
199
+ }
200
+ ) ;
204
201
205
202
return parsed ;
206
203
}
0 commit comments