UNPKG

8.02 kBJavaScriptView Raw
1/**
2 * @license MIT
3 * Copyright (c) 2016 Craig Monro (cmroanirgo)
4 **/
5
6/*
7* Not for public consumption. plugins *do* have access to this however, but it is assumed for read-only purposes
8*
9* Other developers might also think of this as the 'environment' class.
10*
11*/
12"use strict";
13
14var l = require('ergo-utils').log.module('ergo-lib-context');
15var _ = require('ergo-utils')._;
16var fs = require('ergo-utils').fs;
17var path = require('path');
18var FileInfo = require('./fileinfo');
19
20
21function _mergeRuntimeOptions(options) {
22 // this is called (eg. from 'build') to incorporate any commandline options, eg --verbose --quiet, etc
23 var mergeable = "verbose,quiet,debug,log_options".split(',');
24 var _this = this;
25 var o = {}
26 mergeable.forEach(function(prop) {
27 if (_.isDefined(options[prop]))
28 o[prop] = options[prop];
29 });
30 _.extend(_this.config, o);
31 l.init(_this.config);
32
33 // allow the config's 'runtime_options' section override command line
34 return _.extend(options, this.config.runtime_options);
35}
36
37
38function _getName(filename) {
39 return path.basename(filename).toLowerCase();
40}
41
42
43function _filterLog(fileInfo, regExp) {
44 try {
45 return regExp.test(fileInfo.relPath);
46 }
47 catch (e) {
48 l.logw("Unexpected failure in applying log filter: " + _.niceStackTrace(e));
49 return false;
50 }
51}
52
53
54function Context(config, config_path) {
55 this.config = config;
56 this.files = []; // of FileInfo (see ./fileinfo.js). Files, images & movies are here too
57 this.partials = {}; // a cache of name => FileInfo
58 this.layouts = {}; // a cache of name => FileInfo
59 /*this.theme = {
60 partials: {} // a cache of name => FileInfo
61 , layouts: {} // a cache of name => FileInfo
62 } */
63 this.themeconfig = { // settings for themes. This is set by the theme itself. This is an example only:
64 // name: 'Bootstrap - Clean Blog'
65
66 // Where to grab it
67 //, url: 'https://github.com/BlackrockDigital/startbootstrap-clean-blog/archive/gh-pages.zip'
68
69 // What it looks like & where it's demo is
70 //, preview_image: 'https://startbootstrap.com/img/templates/clean-blog.jpg'
71 //, demo_url: 'https://blackrockdigital.github.io/startbootstrap-clean-blog/'
72
73 // Which sub-folders are important & should be copied verbatim. '*' indicates every folder below root should be copied
74 asset_paths: '*' // eg, 'images,stylesheets', or 'assets', or '*' (all sub folders), OR as an Array object
75 };
76
77 this.fields = {}; // this can be updated by plugins
78 this.config_path = config_path;
79 this.base_path = path.dirname(config_path);
80 if (_.isDefined(config['log_options']) && _.isDefined(config.log_options['match_file'])) {
81 var regExp = config.log_options['match_file'];
82 if (!_.isObject(regExp))
83 regExp = new RegExp(regExp, 'i')
84 l.global.setIfConditioner( _filterLog, regExp);
85 }
86
87 // try and load the themeconfig file now
88 var themeconfigjs = path.join(this.getThemePath(), 'theme.ergo.js');
89 if (fs.fileExistsSync(themeconfigjs))
90 {
91 try {
92 _.extend(this.themeconfig, require(themeconfigjs));
93 this.themeconfig.asset_paths = _.toRealArray(this.themeconfig.asset_paths, ',');
94
95 // load in any default_fields into the global context...but at lowest priority
96 if (_.isDefined(this.themeconfig['default_fields'])) {
97 this.config.default_fields = _.extend({}, this.themeconfig['default_fields'], config['default_fields']);
98 }
99 }
100 catch(e) {
101 l.logw("Could not find theme at: " + themeconfigjs);
102 }
103 }
104 else
105 {
106 if (!!this.config.theme)
107 throw new Error("Cannot find theme: " + themeconfigjs)
108 }
109}
110
111Context.prototype.constructor = Context;
112Context.prototype.addFile = function(path, stats) { // returns a Promise
113 var _this = this;
114 var fi = new FileInfo(path, stats, this);
115 var ignore = fi.usage == fi.USAGE.IGNORE;
116 if (!ignore) {
117 // check the existing files to see if something else exists that:
118 // a) is a theme file and should be removed (/ignored)
119 // b) isn't a theme file, but this file is (& should be ignored)
120
121 // a normal file. compare each other file by their expected destination for a collision
122 for (var i=_this.files.length-1; i>=0; i--) {
123 var fi2 = _this.files[i];
124 if (fi2.destPath == fi.destPath) {
125 // conflict.
126 if (fi2.isTheme) { // it needs removing
127 l.logIf(fi2, 2, "Conflicting files: '" + (fi.isTheme?"<theme>/":"")+ fi.relPath + "' and '" + (fi2.isTheme?"<theme>/":"")+ fi2.relPath + "'. The latter is removed from processing")
128 _this.files.splice(i, 1);
129 }
130 else
131 if (fi.isTheme) { // it needs removing
132 ignore = true;
133 l.logIf(fi2, 2, "Conflicting files: '" + (fi.isTheme?"<theme>/":"")+ fi.relPath + "' and '" + (fi2.isTheme?"<theme>/":"")+ fi2.relPath + "'. The former is ignored from processing")
134 }
135 else
136 throw new Error("Unexpected file collision with '" + (fi.isTheme?"<theme>/":"")+ fi.relPath + "' and '" + (fi2.isTheme?"<theme>/":"")+ fi2.relPath + "'. Don't know how to proceed.")
137 break;
138 }
139 }
140
141 if (!ignore && (fi.isPartial || fi.isLayout))
142 {
143 var name = _getName(fi.path);
144 fi.canSave = false;
145 var list = fi.isPartial ? _this.partials : _this.layouts;
146
147 if (!fi.isTheme || !_.isDefined(list[name]))
148 list[name] = fi;
149 else
150 ignore = true;
151 }
152
153 if (!ignore)
154 l.logIf(fi, 2, "No conflicting files for: '" + fi.relPath + "'. destPath='"+fi.destPath+"'")
155 }
156
157 if (ignore) {
158 l.logIf(fi, 2, "Ignored : '" + fi.relPath + "'")
159 return Promise.resolve(false);
160 }
161
162 _this.files.push(fi); // EVERYTHING renderable/copy-able goes into the common list (including partials & templates)
163
164
165 return Promise.resolve(true);
166}
167
168Context.prototype.getFileInfoByRelPath = function(relPath) {
169 var ar = this.files.filter(function(fi) { return fi.relPath == relPath;})
170 if (ar.length)
171 return ar[0];
172 else
173 return null;
174};
175
176
177Context.prototype.getBasePath = function() { return this.base_path; };
178Context.prototype.getRelSourcePath = function() { return fs.fixupPathSep(this.config.source_path) || "source"; }
179Context.prototype.getRelLayoutsPath = function() { return fs.fixupPathSep(this.config.layouts_path) || "_layouts"; };
180Context.prototype.getRelPartialsPath = function() { return fs.fixupPathSep(this.config.partials_path) || "_partials"; };
181Context.prototype.getRelThemesRootPath = function() { return fs.fixupPathSep(this.config.themes_path) || "_themes" };
182Context.prototype.getRelThemePath = function() { return path.join(this.getRelThemesRootPath(), this.config.theme || '_no_theme'); };
183Context.prototype.getRelOutPath = function() { return fs.fixupPathSep(this.config.out_path) || "output"; };
184Context.prototype.getRelPluginsPath = function() { return fs.fixupPathSep(this.config.plugins_path) || "_plugins"; };
185
186Context.prototype.getSourcePath = function() { return path.join(this.base_path, this.getRelSourcePath()); }
187Context.prototype.getLayoutsPath = function() { return path.join(this.base_path, this.getRelLayoutsPath()); }
188Context.prototype.getPartialsPath = function() { return path.join(this.base_path, this.getRelPartialsPath()); }
189Context.prototype.getPluginsPath = function() { return path.join(this.base_path, this.getRelPluginsPath()); }
190Context.prototype.getThemesRootPath = function() { return path.join(this.base_path, this.getRelThemesRootPath()); }
191Context.prototype.getThemePath = function() { return path.join(this.base_path, this.getRelThemePath()); }
192Context.prototype.getThemeLayoutsPath = function() { return path.join(this.getThemePath(), "_layouts"); };
193Context.prototype.getThemePartialsPath = function() { return path.join(this.getThemePath(), "_partials"); };
194Context.prototype.getOutPath = function() { return path.join(this.base_path, this.getRelOutPath()); }
195//Context.prototype.filenameFixup = function(file) { return _fixupFileChars(file, this.config['filename_space_char']) }
196Context.prototype.mergeRuntimeOptions = _mergeRuntimeOptions;
197Context.prototype.lookupLayoutByName = function(name) {
198 name = name || this.config.post_types[this.config.default_post_type]['layout'];
199 return this.layouts[_getName(name)];
200};
201
202module.exports = Context;
203