patternlab-node
Version:
Pattern Lab is a collection of tools to help you create atomic design systems. This is the node command line interface (CLI).
189 lines (157 loc) • 6.66 kB
JavaScript
// special shoutout to Geoffrey Pursell for single-handedly making Pattern Lab Node Pattern Engines possible!
;
var path = require('path');
var diveSync = require('diveSync');
const chalk = require('chalk');
var engineMatcher = /^patternengine-node-(.*)$/;
var enginesDirectories = [
{
displayName: 'the core',
path: path.resolve(__dirname, '..', '..', 'node_modules')
},
{
displayName: 'the edition or test directory',
path: path.join(process.cwd(), 'node_modules')
}
];
var PatternEngines; // the main export object
var engineNameForExtension; // generated mapping of extension to engine name
// free "private" functions, for internal setup only
// given a path: return the engine name if the path points to a valid engine
// module directory, or false if it doesn't
function isEngineModule(filePath) {
var baseName = path.basename(filePath);
var engineMatch = baseName.match(engineMatcher);
if (engineMatch) { return engineMatch[1]; }
return false;
}
function findEngineModulesInDirectory(dir) {
var foundEngines = [];
diveSync(dir, {
recursive: false,
directories: true
}, function (err, filePath) {
if (err) { throw err; }
var foundEngineName = isEngineModule(filePath);
if (foundEngineName) {
foundEngines.push({
name: foundEngineName,
modulePath: filePath
});
}
});
return foundEngines;
}
// Try to load engines! We scan for engines at each path specified above. This
// function is kind of a big deal.
function loadAllEngines(enginesObject) {
enginesDirectories.forEach(function (engineDirectory) {
var enginesInThisDir = findEngineModulesInDirectory(engineDirectory.path);
console.log(chalk.bold(`Loading engines from ${engineDirectory.displayName}...\n`));
// find all engine-named things in this directory and try to load them,
// unless it's already been loaded.
enginesInThisDir.forEach(function (engineDiscovery) {
var errorMessage;
var successMessage = chalk.green("good to go");
try {
// give it a try! load 'er up. But not if we already have, of course.
if (enginesObject[engineDiscovery.name]) {
throw new Error("already loaded, skipping.");
}
enginesObject[engineDiscovery.name] = require(engineDiscovery.modulePath);
} catch (err) {
errorMessage = err.message;
} finally {
// report on the status of the engine, one way or another!
console.log(` ${engineDiscovery.name}:`, errorMessage ? chalk.red(errorMessage) : successMessage);
}
});
console.log('');
});
// Complain if for some reason we haven't loaded any engines.
if (Object.keys(enginesObject).length === 0) {
throw new Error('No engines loaded! Something is seriously wrong.');
}
console.log(chalk.bold('Done loading engines.\n'));
}
// produce a mapping between file extension and engine name for each of the
// loaded engines
function createFileExtensionToEngineNameMap(enginesObject) {
var mapping = {};
Object.keys(enginesObject).forEach(function (engineName) {
var extensionForEngine = enginesObject[engineName].engineFileExtension;
mapping[extensionForEngine] = engineName;
});
return mapping;
}
//
// PatternEngines: the main export of this module
//
// It's an Object/hash of all loaded pattern engines, empty at first. My
// intention here is to make this return an object that can be used to obtain
// any loaded PatternEngine by addressing them like this:
//
// var PatternEngines = require('./pattern_engines/pattern_engines');
// var Mustache = PatternEngines['mustache'];
//
// Object.create lets us create an object with a specified prototype. We want
// this here because we would like the object's "own properties" to include
// only the engine names so we can easily iterate over them; all the handy
// methods and properites below should therefore be on its prototype.
PatternEngines = Object.create({
getEngineNameForPattern: function (pattern) {
// avoid circular dependency by putting this in here. TODO: is this slow?
var of = require('./object_factory');
if (pattern instanceof of.Pattern && typeof pattern.fileExtension === 'string' && pattern.fileExtension) {
return engineNameForExtension[pattern.fileExtension];
}
// otherwise, assume it's a plain mustache template string and act
// accordingly
return 'mustache';
},
getEngineForPattern: function (pattern) {
if (pattern.isPseudoPattern) {
return this.getEngineForPattern(pattern.basePattern);
} else {
var engineName = this.getEngineNameForPattern(pattern);
return this[engineName];
}
},
getSupportedFileExtensions: function () {
var engineNames = Object.keys(PatternEngines);
return engineNames.map(function (engineName) {
return PatternEngines[engineName].engineFileExtension;
});
},
isFileExtensionSupported: function (fileExtension) {
var supportedExtensions = PatternEngines.getSupportedFileExtensions();
return (supportedExtensions.lastIndexOf(fileExtension) !== -1);
},
// given a filename, return a boolean: whether or not the filename indicates
// that the file is pseudopattern JSON
isPseudoPatternJSON: function (filename) {
var extension = path.extname(filename);
return (extension === '.json' && filename.indexOf('~') > -1);
},
// takes a filename string, not a full path; a basename (plus extension)
// ignore _underscored patterns, dotfiles, and anything not recognized by a
// loaded pattern engine. Pseudo-pattern .json files ARE considered to be
// pattern files!
isPatternFile: function (filename) {
// skip hidden patterns/files without a second thought
var extension = path.extname(filename);
if (filename.charAt(0) === '.' ||
(extension === '.json' && !PatternEngines.isPseudoPatternJSON(filename))) {
return false;
}
// not a hidden pattern, let's dig deeper
var supportedPatternFileExtensions = PatternEngines.getSupportedFileExtensions();
return (supportedPatternFileExtensions.lastIndexOf(extension) !== -1 ||
PatternEngines.isPseudoPatternJSON(filename));
}
});
// load up the engines we found
loadAllEngines(PatternEngines);
// mapping of file extensions to engine names, for lookup use
engineNameForExtension = createFileExtensionToEngineNameMap(PatternEngines);
module.exports = PatternEngines;