1
1
// special shoutout to Geoffrey Pursell for single-handedly making Pattern Lab Node Pattern Engines possible!
2
2
'use strict' ;
3
+ const { existsSync, lstatSync, readdirSync} = require ( 'fs' ) ;
3
4
const path = require ( 'path' ) ;
4
- const diveSync = require ( 'diveSync' ) ;
5
5
const chalk = require ( 'chalk' ) ;
6
6
const engineMatcher = / ^ p a t t e r n e n g i n e - n o d e - ( .* ) $ / ;
7
- const enginesDirectories = [
8
- {
9
- displayName : 'the core' ,
10
- path : path . resolve ( __dirname , '..' , '..' , 'node_modules' )
11
- } ,
12
- {
13
- displayName : 'the edition or test directory' ,
14
- path : path . join ( process . cwd ( ) , 'node_modules' )
15
- }
16
- ] ;
7
+ const scopeMatch = / ^ @ ( . * ) $ / ;
8
+ const isDir = fPath => lstatSync ( fPath ) . isDirectory ( ) ;
9
+
10
+ const enginesDirectories = [ {
11
+ displayName : 'the core' ,
12
+ path : path . resolve ( __dirname , '..' , '..' , 'node_modules' )
13
+ } , {
14
+ displayName : 'the edition or test directory' ,
15
+ path : path . join ( process . cwd ( ) , 'node_modules' )
16
+ } ] ;
17
17
18
18
// given a path: return the engine name if the path points to a valid engine
19
19
// module directory, or false if it doesn't
20
20
function isEngineModule ( filePath ) {
21
21
const baseName = path . basename ( filePath ) ;
22
22
const engineMatch = baseName . match ( engineMatcher ) ;
23
-
23
+
24
24
if ( engineMatch ) { return engineMatch [ 1 ] ; }
25
25
return false ;
26
26
}
27
27
28
- function findEngineModulesInDirectory ( dir ) {
29
- const foundEngines = [ ] ;
28
+ /**
29
+ * @name isScopedPackage
30
+ * @desc Checks whether a path in modules belongs to a scoped package
31
+ * @param {string } filePath - The pathname to check
32
+ * @return {Boolean } - Returns a bool when found, false othersie
33
+ */
34
+ function isScopedPackage ( filePath ) {
35
+ const baseName = path . basename ( filePath ) ;
36
+ return scopeMatch . test ( baseName ) ;
37
+ }
30
38
31
- diveSync ( dir , {
32
- recursive : false ,
33
- directories : true
34
- } , function ( err , filePath ) {
35
- if ( err ) { throw err ; }
36
- const foundEngineName = isEngineModule ( filePath ) ;
37
- if ( foundEngineName ) {
38
- foundEngines . push ( {
39
- name : foundEngineName ,
40
- modulePath : filePath
41
- } ) ;
42
- }
43
- } ) ;
39
+ /**
40
+ * @name resolveEngines
41
+ * @desc Creates an array of all available patternlab engines
42
+ * @param {string } dir - The directory to search for engines and scoped engines)
43
+ * @return {Array<Engine> } An array of engine objects
44
+ */
45
+ function resolveEngines ( dir ) {
46
+
47
+ // Guard against non-existent directories.
48
+ if ( ! existsSync ( dir ) ) {
49
+ return [ ] ; // Silence is golden …
50
+ }
51
+
52
+ /**
53
+ * @name walk
54
+ * @desc Traverse the given path and gather possible engines
55
+ * @param {string } fPath - The file path to traverse
56
+ * @param {Array<Engine> } engines - An array of engines from the inner most matches
57
+ * @return {Array<Engine> } - The final array of engines
58
+ */
59
+ const walk = ( fPath , engines ) => {
60
+
61
+ /**
62
+ * @name dirList
63
+ * @desc A list of all directories in the given path
64
+ * @type {Array<string> }
65
+ */
66
+ const dirList = readdirSync ( fPath ) . filter ( p => isDir ( path . join ( fPath , p ) ) ) ;
67
+
68
+ /**
69
+ * @name e
70
+ * @desc For the current dir get all engines
71
+ * @type {Array<Engine> }
72
+ */
73
+ const e = engines . concat ( dirList
74
+ . filter ( isEngineModule )
75
+ . map ( engine => {
76
+ return {
77
+ name : isEngineModule ( engine ) ,
78
+ modulePath : path . join ( fPath , engine )
79
+ }
80
+ } )
81
+ ) ;
82
+
83
+ /**
84
+ * 1. Flatten all engines from inner recursions and current dir
85
+ * 2. Filter the dirList for scoped packages
86
+ * 3. Map over every scoped package and recurse into it to find scoped engines
87
+ */
88
+ return [ ] . concat (
89
+ ...e ,
90
+ ...dirList
91
+ . filter ( isScopedPackage ) // 2
92
+ . map ( scope => walk ( path . join ( fPath , scope ) , e ) ) // 3
93
+ ) ;
94
+ } ;
95
+
96
+ return walk ( dir , [ ] ) ;
97
+ }
44
98
99
+ function findEngineModulesInDirectory ( dir ) {
100
+ const foundEngines = resolveEngines ( dir )
45
101
return foundEngines ;
46
102
}
47
103
@@ -61,18 +117,18 @@ function findEngineModulesInDirectory(dir) {
61
117
// methods and properites below should therefore be on its prototype.
62
118
63
119
const PatternEngines = Object . create ( {
64
-
120
+
65
121
loadAllEngines : function ( patternLabConfig ) {
66
122
var self = this ;
67
-
123
+
68
124
// Try to load engines! We scan for engines at each path specified above. This
69
125
// function is kind of a big deal.
70
126
enginesDirectories . forEach ( function ( engineDirectory ) {
71
127
const enginesInThisDir = findEngineModulesInDirectory ( engineDirectory . path ) ;
72
128
if ( patternLabConfig . debug ) {
73
129
console . log ( chalk . bold ( `Loading engines from ${ engineDirectory . displayName } ...\n` ) ) ;
74
130
}
75
-
131
+
76
132
// find all engine-named things in this directory and try to load them,
77
133
// unless it's already been loaded.
78
134
enginesInThisDir . forEach ( function ( engineDiscovery ) {
@@ -81,7 +137,7 @@ const PatternEngines = Object.create({
81
137
if ( patternLabConfig . debug ) {
82
138
chalk . green ( successMessage ) ;
83
139
}
84
-
140
+
85
141
try {
86
142
// Give it a try! load 'er up. But not if we already have,
87
143
// of course. Also pass the pattern lab config object into
@@ -108,7 +164,7 @@ const PatternEngines = Object.create({
108
164
} ) ;
109
165
console . log ( '' ) ;
110
166
} ) ;
111
-
167
+
112
168
// Complain if for some reason we haven't loaded any engines.
113
169
if ( Object . keys ( self ) . length === 0 ) {
114
170
throw new Error ( 'No engines loaded! Something is seriously wrong.' ) ;
@@ -117,7 +173,7 @@ const PatternEngines = Object.create({
117
173
console . log ( chalk . bold ( 'Done loading engines.\n' ) ) ;
118
174
}
119
175
} ,
120
-
176
+
121
177
getEngineNameForPattern : function ( pattern ) {
122
178
// avoid circular dependency by putting this in here. TODO: is this slow?
123
179
const of = require ( './object_factory' ) ;
@@ -126,7 +182,7 @@ const PatternEngines = Object.create({
126
182
const engineNames = Object . keys ( this ) ;
127
183
for ( let i = 0 ; i < engineNames . length ; i ++ ) {
128
184
const engine = this [ engineNames [ i ] ] ;
129
-
185
+
130
186
if ( Array . isArray ( engine . engineFileExtension ) ) {
131
187
if ( engine . engineFileExtension . includes ( pattern . fileExtension ) ) {
132
188
return engine . engineName ;
@@ -139,12 +195,12 @@ const PatternEngines = Object.create({
139
195
}
140
196
}
141
197
}
142
-
198
+
143
199
// otherwise, assume it's a plain mustache template string and act
144
200
// accordingly
145
201
return 'mustache' ;
146
202
} ,
147
-
203
+
148
204
getEngineForPattern : function ( pattern ) {
149
205
if ( pattern . isPseudoPattern ) {
150
206
return this . getEngineForPattern ( pattern . basePattern ) ;
@@ -153,7 +209,7 @@ const PatternEngines = Object.create({
153
209
return this [ engineName ] ;
154
210
}
155
211
} ,
156
-
212
+
157
213
// combine all found engines into a single array of supported extensions
158
214
getSupportedFileExtensions : function ( ) {
159
215
const engineNames = Object . keys ( PatternEngines ) ;
@@ -162,19 +218,19 @@ const PatternEngines = Object.create({
162
218
} ) ;
163
219
return [ ] . concat . apply ( [ ] , allEnginesExtensions ) ;
164
220
} ,
165
-
221
+
166
222
isFileExtensionSupported : function ( fileExtension ) {
167
223
const supportedExtensions = PatternEngines . getSupportedFileExtensions ( ) ;
168
224
return ( supportedExtensions . lastIndexOf ( fileExtension ) !== - 1 ) ;
169
225
} ,
170
-
226
+
171
227
// given a filename, return a boolean: whether or not the filename indicates
172
228
// that the file is pseudopattern JSON
173
229
isPseudoPatternJSON : function ( filename ) {
174
230
const extension = path . extname ( filename ) ;
175
231
return ( extension === '.json' && filename . indexOf ( '~' ) > - 1 ) ;
176
232
} ,
177
-
233
+
178
234
// takes a filename string, not a full path; a basename (plus extension)
179
235
// ignore _underscored patterns, dotfiles, and anything not recognized by a
180
236
// loaded pattern engine. Pseudo-pattern .json files ARE considered to be
@@ -183,14 +239,14 @@ const PatternEngines = Object.create({
183
239
// skip hidden patterns/files without a second thought
184
240
const extension = path . extname ( filename ) ;
185
241
if ( filename . charAt ( 0 ) === '.' ||
186
- ( extension === '.json' && ! PatternEngines . isPseudoPatternJSON ( filename ) ) ) {
242
+ ( extension === '.json' && ! PatternEngines . isPseudoPatternJSON ( filename ) ) ) {
187
243
return false ;
188
244
}
189
-
245
+
190
246
// not a hidden pattern, let's dig deeper
191
247
const supportedPatternFileExtensions = PatternEngines . getSupportedFileExtensions ( ) ;
192
248
return ( supportedPatternFileExtensions . lastIndexOf ( extension ) !== - 1 ||
193
- PatternEngines . isPseudoPatternJSON ( filename ) ) ;
249
+ PatternEngines . isPseudoPatternJSON ( filename ) ) ;
194
250
}
195
251
} ) ;
196
252
0 commit comments