UNPKG

6.41 kBJavaScriptView Raw
1/*!
2* Srcerer, utils
3* Copyright(c) 2010-2017 Jesse Tijnagel (Guilala)
4* MIT Licensed
5*/
6
7/*jslint node: true */
8"use strict";
9
10const fs = require("fs");
11const hash = require("string-hash");
12const path = require("path");
13const untildify = require("untildify");
14
15var modificationCache = {};
16var jsonLoaded = {};
17
18// object helpers
19const obj = {
20 // object child key count
21 keyCount: function (obj) {
22 if (obj.constructor === Object) {
23 return Object.keys(obj).length;
24 } else return 0;
25 },
26
27 isArray: function (obj) {
28 return obj && obj.constructor === Array;
29 },
30
31 // shallow merge to first object
32 merge: function (objA, objB) {
33 for (var key in objB) objA[key] = objB[key];
34 return objA;
35 },
36
37 // shallow merge to new object
38 mergeTo: function (objA, objB) {
39 var result = {};
40 for (var keyA in objA) result[keyA] = objA[keyA];
41 for (var keyB in objB) result[keyB] = objB[keyB];
42 return result;
43 },
44
45 // wrap array when isn't array
46 arrayify: function (obj) {
47 if (Array.isArray(obj)) {
48 return obj;
49 } else {
50 return [obj];
51 }
52 }
53};
54
55// normalise path strings
56const parsePaths = function(paths) {
57 paths = obj.arrayify(paths);
58 paths.forEach((segment, index, pathsArray) => {
59 if(segment) {
60 pathsArray[index] = untildify(segment);
61 } else {
62 pathsArray[index] = "";
63 }
64 });
65 return path.resolve.apply({}, paths);
66};
67
68// fs directorie helpers
69const dir = {
70 list: function (Path) {
71 if (fs.existsSync(Path)) {
72 return fs.readdirSync(Path);
73 } else return false;
74 },
75
76 getDirectories: function(srcPath){
77 return fs.readdirSync(srcPath).filter(function(file) {
78 return fs.statSync(parsePaths([srcPath, file])).isDirectory();
79 });
80 }
81};
82
83// basic html generator
84const dombo = function(){
85 var innerHead = [""];
86
87 var tag = (type, content, attr) => {
88 var chunks = [`<${type}`];
89
90 for(var key in attr) {
91 if(attr[key]) {
92 chunks.push(` ${key}="${attr[key]}"`);
93 } else {
94 chunks.push(` ${key}`);
95 }
96 }
97
98 if(content === false) {
99 chunks.push("/>");
100 } else {
101 chunks.push(`>${content}</${type}>`);
102 }
103
104 return chunks.join("");
105 };
106
107 this.head = function (type, content, attr) {
108 innerHead.push(tag(type, content, attr));
109 };
110
111 this.comment = function(content) {
112 innerHead.push("<!-- " + content + " -->");
113 };
114
115 this.style = function (content) {
116 innerHead.push(tag("style", content, {"type": "text/css"}));
117 };
118
119 const untildify = require('untildify');this.script = function (content) {
120 innerHead.push(tag("script", content + "\n", {"type": "text/javascript"}));
121 };
122
123 this.parse = function(){
124 innerHead.push("");
125 var docType = "<!DOCTYPE html>\n";
126 var head = `${tag("head", innerHead.join("\n"))}`;
127 var body = "<body></body>";
128 var innerHtml = `${head}${body}`;
129 return `${docType}${tag("html", innerHtml, {lang:"en"})}\n`;
130 };
131};
132
133// call async functions sequentially, finish when they are done
134const que = function (context) {
135 var self = this;
136 var row = [];
137
138 var next = () => {
139 if (row.length) {
140 new Promise((resolve, reject) => {
141 row.shift().call(context || self, resolve, reject);
142 }).then(next);
143 }
144 };
145
146 this.add = (step) => {
147 if (step && step.call) {
148 row.push(step);
149 } else {
150 console.error("util.que: not a function");
151 }
152
153 return self;
154 };
155
156 this.then = (action) => {
157 self.add(action);
158 next();
159
160 return self;
161 };
162
163 return self;
164};
165
166// call async functions simultaneously, finish when they are done
167const all = function (context) {
168 var self = this;
169 var list = [];
170
171 this.add = (action) => {
172 if (action && action.call) {
173 list.push(action);
174 } else {
175 console.error("util.all, add: not a function");
176 }
177
178 return self;
179 };
180
181 this.then = (finalAction) => {
182 new Promise((resolve) => {
183 var count = list.length;
184 if(count) {
185 list.forEach((action) => {
186 action.call(context || self, () => {
187 // individual resolve
188 count--;
189 // total resolve
190 if (count === 0) {
191 resolve();
192 }
193 });
194 });
195 } else resolve();
196 }).then(() => {
197 if(finalAction && finalAction.call) {
198 finalAction.call(context || self);
199 }
200 });
201
202 return self;
203 };
204
205 return self;
206};
207
208// is file modified since process start or last call
209const modified = function (path, name) {
210 return new Promise((resolve, reject) => {
211 fs.stat(path, (err, stats) => {
212 if(err){
213 reject(err);
214 } else {
215 var modified = new Date(stats.mtime).getTime();
216 if (modificationCache.hasOwnProperty(name) && modified <= modificationCache[name]) {
217 modified = false;
218 } else {
219 modificationCache[name] = modified;
220 }
221 resolve(modified);
222 }
223 });
224 });
225};
226
227const clearModificationCache = function() {
228 modificationCache = {};
229};
230
231// load, and cache local json
232const localJson = function(path){
233 return new Promise((resolve, reject) => {
234 var name = hash(path);
235 modified(path, name).then((isModified) => {
236 if(isModified){
237 fs.readFile(path, 'utf8', function (err, data) {
238 if (err) {
239 console.error(err);
240 reject(err);
241 } else {
242 try {
243 resolve(jsonLoaded[name] = JSON.parse(data));
244 } catch (parseError) {
245 console.error(parseError);
246 reject(parseError);
247 }
248 }
249 });
250 } else {
251 resolve(jsonLoaded[name]);
252 }
253 }, (err) => {
254 reject(err);
255 });
256 });
257};
258
259// map to module export
260module.exports = {
261 obj: obj,
262 dir: dir,
263 parsePaths: parsePaths,
264 dombo: dombo,
265 que: que,
266 all: all,
267 modified: modified,
268 clearModificationCache: clearModificationCache,
269 localJson: localJson
270};
271