Skip to content

Commit aa4ddb8

Browse files
committed
Adding better traverse for pattern engines
1 parent 80a8017 commit aa4ddb8

File tree

1 file changed

+99
-43
lines changed

1 file changed

+99
-43
lines changed

core/lib/pattern_engines.js

Lines changed: 99 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,103 @@
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');
34
const path = require('path');
4-
const diveSync = require('diveSync');
55
const chalk = require('chalk');
66
const engineMatcher = /^patternengine-node-(.*)$/;
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
2020
function 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

63119
const 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

Comments
 (0)