11// special shoutout to Geoffrey Pursell for single-handedly making Pattern Lab Node Pattern Engines possible!
22'use strict' ;
3+ const { existsSync, lstatSync, readdirSync} = require ( 'fs' ) ;
34const path = require ( 'path' ) ;
4- const diveSync = require ( 'diveSync' ) ;
55const chalk = require ( 'chalk' ) ;
66const 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+ } ] ;
1717
1818// given a path: return the engine name if the path points to a valid engine
1919// module directory, or false if it doesn't
2020function isEngineModule ( filePath ) {
2121 const baseName = path . basename ( filePath ) ;
2222 const engineMatch = baseName . match ( engineMatcher ) ;
23-
23+
2424 if ( engineMatch ) { return engineMatch [ 1 ] ; }
2525 return false ;
2626}
2727
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+ }
3038
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+ }
4498
99+ function findEngineModulesInDirectory ( dir ) {
100+ const foundEngines = resolveEngines ( dir )
45101 return foundEngines ;
46102}
47103
@@ -61,18 +117,18 @@ function findEngineModulesInDirectory(dir) {
61117// methods and properites below should therefore be on its prototype.
62118
63119const PatternEngines = Object . create ( {
64-
120+
65121 loadAllEngines : function ( patternLabConfig ) {
66122 var self = this ;
67-
123+
68124 // Try to load engines! We scan for engines at each path specified above. This
69125 // function is kind of a big deal.
70126 enginesDirectories . forEach ( function ( engineDirectory ) {
71127 const enginesInThisDir = findEngineModulesInDirectory ( engineDirectory . path ) ;
72128 if ( patternLabConfig . debug ) {
73129 console . log ( chalk . bold ( `Loading engines from ${ engineDirectory . displayName } ...\n` ) ) ;
74130 }
75-
131+
76132 // find all engine-named things in this directory and try to load them,
77133 // unless it's already been loaded.
78134 enginesInThisDir . forEach ( function ( engineDiscovery ) {
@@ -81,7 +137,7 @@ const PatternEngines = Object.create({
81137 if ( patternLabConfig . debug ) {
82138 chalk . green ( successMessage ) ;
83139 }
84-
140+
85141 try {
86142 // Give it a try! load 'er up. But not if we already have,
87143 // of course. Also pass the pattern lab config object into
@@ -108,7 +164,7 @@ const PatternEngines = Object.create({
108164 } ) ;
109165 console . log ( '' ) ;
110166 } ) ;
111-
167+
112168 // Complain if for some reason we haven't loaded any engines.
113169 if ( Object . keys ( self ) . length === 0 ) {
114170 throw new Error ( 'No engines loaded! Something is seriously wrong.' ) ;
@@ -117,7 +173,7 @@ const PatternEngines = Object.create({
117173 console . log ( chalk . bold ( 'Done loading engines.\n' ) ) ;
118174 }
119175 } ,
120-
176+
121177 getEngineNameForPattern : function ( pattern ) {
122178 // avoid circular dependency by putting this in here. TODO: is this slow?
123179 const of = require ( './object_factory' ) ;
@@ -126,7 +182,7 @@ const PatternEngines = Object.create({
126182 const engineNames = Object . keys ( this ) ;
127183 for ( let i = 0 ; i < engineNames . length ; i ++ ) {
128184 const engine = this [ engineNames [ i ] ] ;
129-
185+
130186 if ( Array . isArray ( engine . engineFileExtension ) ) {
131187 if ( engine . engineFileExtension . includes ( pattern . fileExtension ) ) {
132188 return engine . engineName ;
@@ -139,12 +195,12 @@ const PatternEngines = Object.create({
139195 }
140196 }
141197 }
142-
198+
143199 // otherwise, assume it's a plain mustache template string and act
144200 // accordingly
145201 return 'mustache' ;
146202 } ,
147-
203+
148204 getEngineForPattern : function ( pattern ) {
149205 if ( pattern . isPseudoPattern ) {
150206 return this . getEngineForPattern ( pattern . basePattern ) ;
@@ -153,7 +209,7 @@ const PatternEngines = Object.create({
153209 return this [ engineName ] ;
154210 }
155211 } ,
156-
212+
157213 // combine all found engines into a single array of supported extensions
158214 getSupportedFileExtensions : function ( ) {
159215 const engineNames = Object . keys ( PatternEngines ) ;
@@ -162,19 +218,19 @@ const PatternEngines = Object.create({
162218 } ) ;
163219 return [ ] . concat . apply ( [ ] , allEnginesExtensions ) ;
164220 } ,
165-
221+
166222 isFileExtensionSupported : function ( fileExtension ) {
167223 const supportedExtensions = PatternEngines . getSupportedFileExtensions ( ) ;
168224 return ( supportedExtensions . lastIndexOf ( fileExtension ) !== - 1 ) ;
169225 } ,
170-
226+
171227 // given a filename, return a boolean: whether or not the filename indicates
172228 // that the file is pseudopattern JSON
173229 isPseudoPatternJSON : function ( filename ) {
174230 const extension = path . extname ( filename ) ;
175231 return ( extension === '.json' && filename . indexOf ( '~' ) > - 1 ) ;
176232 } ,
177-
233+
178234 // takes a filename string, not a full path; a basename (plus extension)
179235 // ignore _underscored patterns, dotfiles, and anything not recognized by a
180236 // loaded pattern engine. Pseudo-pattern .json files ARE considered to be
@@ -183,14 +239,14 @@ const PatternEngines = Object.create({
183239 // skip hidden patterns/files without a second thought
184240 const extension = path . extname ( filename ) ;
185241 if ( filename . charAt ( 0 ) === '.' ||
186- ( extension === '.json' && ! PatternEngines . isPseudoPatternJSON ( filename ) ) ) {
242+ ( extension === '.json' && ! PatternEngines . isPseudoPatternJSON ( filename ) ) ) {
187243 return false ;
188244 }
189-
245+
190246 // not a hidden pattern, let's dig deeper
191247 const supportedPatternFileExtensions = PatternEngines . getSupportedFileExtensions ( ) ;
192248 return ( supportedPatternFileExtensions . lastIndexOf ( extension ) !== - 1 ||
193- PatternEngines . isPseudoPatternJSON ( filename ) ) ;
249+ PatternEngines . isPseudoPatternJSON ( filename ) ) ;
194250 }
195251} ) ;
196252
0 commit comments