1
1
import { existsSync , readdirSync , readFileSync } from "fs" ;
2
2
import { join } from "path" ;
3
3
4
- import { CategoryType , RawSnippetType , SnippetType } from "../src/types" ;
4
+ import { RawSnippetType , SnippetType } from "../src/types" ;
5
5
import { isCorrectType } from "../src/utils/objectUtils" ;
6
6
import { raise } from "../src/utils/raise" ;
7
7
import { reverseSlugify , slugify } from "../src/utils/slugify" ;
8
8
9
- const crlfRegex = / \r \n / gm;
9
+ interface ParseLanguageResponse {
10
+ name : string ;
11
+ icon : string ;
12
+ categories : Array < {
13
+ name : string ;
14
+ snippets : Array < SnippetType > ;
15
+ } > ;
16
+ subLanguages : ParseLanguageResponse [ ] ;
17
+ }
18
+
19
+ interface ParseCategoryResponse {
20
+ name : string ;
21
+ snippets : Array < SnippetType > ;
22
+ }
23
+
10
24
const propertyRegex = / ^ \s + ( [ a - z A - Z ] + ) : \s * ( .+ ) / ;
11
- const headerEndCodeStartRegex = / ^ \s * - - - \s * ` ` ` .* \n / ;
25
+ const headerEndCodeStartRegex = / ^ \s * - - - \s * ` ` ` .* \r ? \ n/ ;
12
26
const codeRegex = / ^ ( .+ ) ` ` ` / s;
13
27
14
- let errored = false ;
28
+ let errored : boolean = false ;
15
29
16
30
function parseSnippet (
17
- snippetPath : string ,
31
+ path : string ,
18
32
name : string ,
19
33
text : string
20
34
) : SnippetType | null {
21
- if ( crlfRegex . exec ( text ) !== null ) {
22
- return raise (
23
- "Found CRLF line endings instead of LF line endings" ,
24
- snippetPath
25
- ) ;
26
- }
27
- let cursor = 0 ;
35
+ let cursor : number = 0 ;
28
36
29
37
const fromCursor = ( ) => text . substring ( cursor ) ;
30
-
31
38
if ( ! fromCursor ( ) . trim ( ) . startsWith ( "---" ) ) {
32
- return raise ( "Missing header start delimiter '---'" , snippetPath ) ;
39
+ return raise ( "Missing header start delimiter '---'" , path ) ;
33
40
}
41
+
34
42
cursor += 3 ;
35
43
36
44
const properties = { } ;
37
45
38
- let match ;
46
+ let match : string [ ] | null ;
39
47
while ( ( match = propertyRegex . exec ( fromCursor ( ) ) ) !== null ) {
40
48
cursor += match [ 0 ] . length ;
41
49
properties [ match [ 1 ] . toLowerCase ( ) ] = match [ 2 ] ;
@@ -49,26 +57,28 @@ function parseSnippet(
49
57
"tags" ,
50
58
] )
51
59
) {
52
- return raise ( "Invalid properties" , snippetPath ) ;
60
+ return raise ( "Invalid properties" , path ) ;
53
61
}
54
62
55
63
if ( slugify ( properties . title ) !== name ) {
56
64
return raise (
57
65
`slugifyed 'title' property doesn't match snippet file name` ,
58
- snippetPath
66
+ path
59
67
) ;
60
68
}
61
69
62
70
match = headerEndCodeStartRegex . exec ( fromCursor ( ) ) ;
63
71
if ( match === null ) {
64
- return raise ( "Missing header end '---' or code start '```'" , snippetPath ) ;
72
+ return raise ( "Missing header end '---' or code start '```'" , path ) ;
65
73
}
74
+
66
75
cursor += match [ 0 ] . length ;
67
76
68
77
match = codeRegex . exec ( fromCursor ( ) ) ;
69
78
if ( match === null ) {
70
- return raise ( "Missing code block end '```'" , snippetPath ) ;
79
+ return raise ( "Missing code block end '```'" , path ) ;
71
80
}
81
+
72
82
const code : string = match [ 1 ] ;
73
83
74
84
return {
@@ -83,56 +93,100 @@ function parseSnippet(
83
93
. split ( "," )
84
94
. map ( ( contributor ) => contributor . trim ( ) )
85
95
. filter ( ( contributor ) => contributor ) ,
86
- code,
96
+ code : code . replace ( / \r \n / g , "\n" ) ,
87
97
} ;
88
98
}
89
99
90
- const snippetPath = "snippets/" ;
91
- export function parseAllSnippets ( ) {
92
- const snippets = { } ;
100
+ function parseCategory ( path : string , name : string ) : ParseCategoryResponse {
101
+ const snippets : SnippetType [ ] = [ ] ;
93
102
94
- for ( const language of readdirSync ( snippetPath ) ) {
95
- const languagePath = join ( snippetPath , language ) ;
96
- const languageIconPath = join ( languagePath , "icon.svg" ) ;
103
+ for ( const snippet of readdirSync ( path ) ) {
104
+ const snippetPath = join ( path , snippet ) ;
105
+ const snippetContent = readFileSync ( snippetPath ) . toString ( ) ;
106
+ const snippetFileName = snippet . slice ( 0 , - 3 ) ;
97
107
98
- if ( ! existsSync ( languageIconPath ) ) {
108
+ const snippetData = parseSnippet (
109
+ snippetPath ,
110
+ snippetFileName ,
111
+ snippetContent
112
+ ) ;
113
+ if ( ! snippetData ) {
99
114
errored = true ;
100
- raise ( `icon for '${ language } ' is missing` ) ;
101
115
continue ;
102
116
}
117
+ snippets . push ( snippetData ) ;
118
+ }
119
+
120
+ return {
121
+ name : reverseSlugify ( name ) ,
122
+ snippets,
123
+ } ;
124
+ }
125
+
126
+ function parseLanguage (
127
+ path : string ,
128
+ name : string ,
129
+ subLanguageOf : string | null = null
130
+ ) : ParseLanguageResponse | null {
131
+ const iconPath = join ( path , "icon.svg" ) ;
103
132
104
- const categories : CategoryType [ ] = [ ] ;
105
-
106
- for ( const category of readdirSync ( languagePath ) ) {
107
- if ( category === "icon.svg" ) continue ;
108
-
109
- const categoryPath = join ( languagePath , category ) ;
110
- const categorySnippets : SnippetType [ ] = [ ] ;
111
-
112
- for ( const snippet of readdirSync ( categoryPath ) ) {
113
- const snippetPath = join ( categoryPath , snippet ) ;
114
- const snippetContent = readFileSync ( snippetPath ) . toString ( ) ;
115
- const snippetFileName = snippet . slice ( 0 , - 3 ) ;
116
- const snippetData = parseSnippet (
117
- snippetPath ,
118
- snippetFileName ,
119
- snippetContent
120
- ) ;
121
- if ( snippetData === null ) {
122
- errored = true ;
123
- continue ;
124
- }
125
- categorySnippets . push ( snippetData ) ;
133
+ if ( ! existsSync ( iconPath ) ) {
134
+ return raise (
135
+ `icon for '${ subLanguageOf ? `${ subLanguageOf } /` : "" } ${ name } ' is missing`
136
+ ) ;
137
+ }
138
+
139
+ const subLanguages : Array < ParseLanguageResponse > = [ ] ;
140
+ const categories : Array < ParseCategoryResponse > = [ ] ;
141
+
142
+ for ( const category of readdirSync ( path ) ) {
143
+ if ( category === "icon.svg" ) continue ;
144
+ const categoryPath = join ( path , category ) ;
145
+
146
+ if ( category . startsWith ( "[" ) && category . endsWith ( "]" ) ) {
147
+ if ( subLanguageOf !== null ) {
148
+ return raise ( "Cannot have more than two level of language nesting" ) ;
126
149
}
127
150
128
- categories . push ( {
129
- categoryName : reverseSlugify ( category ) ,
130
- snippets : categorySnippets ,
131
- } ) ;
151
+ const parsedLanguage = parseLanguage (
152
+ categoryPath ,
153
+ category . slice ( 1 , - 1 ) ,
154
+ name
155
+ ) ;
156
+ if ( ! parsedLanguage ) {
157
+ errored = true ;
158
+ continue ;
159
+ }
160
+ subLanguages . push ( parsedLanguage ) ;
161
+ } else {
162
+ categories . push ( parseCategory ( categoryPath , category ) ) ;
132
163
}
164
+ }
133
165
134
- snippets [ language ] = categories ;
166
+ return {
167
+ name : reverseSlugify ( name ) ,
168
+ icon : iconPath ,
169
+ categories,
170
+ subLanguages,
171
+ } ;
172
+ }
173
+
174
+ export function parseAllSnippets ( ) {
175
+ const snippetPath = "snippets/" ;
176
+
177
+ const languages : ParseLanguageResponse [ ] = [ ] ;
178
+ for ( const language of readdirSync ( snippetPath ) ) {
179
+ const languagePath = join ( snippetPath , language ) ;
180
+ const parsedLanguage = parseLanguage ( languagePath , language ) ;
181
+ if ( ! parsedLanguage ) {
182
+ errored = true ;
183
+ continue ;
184
+ }
185
+ languages . push ( parsedLanguage ) ;
135
186
}
136
187
137
- return [ errored , snippets ] ;
188
+ return {
189
+ errored,
190
+ languages,
191
+ } ;
138
192
}
0 commit comments