UNPKG

28.4 kBJavaScriptView Raw
1"use strict";
2var task_utils_1 = require("./task.utils");
3var fs_1 = require("fs");
4var path_1 = require("path");
5var fs_2 = require("fs");
6var path_2 = require("path");
7var fs_3 = require("fs");
8var Rx = require("rx");
9var path_3 = require("path");
10var fs_4 = require("fs");
11var crypto_1 = require("crypto");
12var fs_5 = require("fs");
13var hd = require("hash-dir");
14var hashDirAsObservable = Rx.Observable.fromNodeCallback(hd);
15var hashFileAsObservable = Rx.Observable.fromNodeCallback(hashFile);
16var lstatAsObservable = Rx.Observable.fromNodeCallback(fs_5.lstat);
17var _ = require("../lodash.custom");
18// todo windows support for .bat files etc
19var supportedTaskFileExtensions = [".js", ".sh"];
20/**
21 * Try to auto-load configuration files
22 * from the users CWD
23 */
24function retrieveDefaultInputFiles(config) {
25 var defaultConfigFiles = ["crossbow.yaml", "crossbow.js", "crossbow.yml", "crossbow.json"];
26 return readInputFiles(defaultConfigFiles, config.cwd);
27}
28exports.retrieveDefaultInputFiles = retrieveDefaultInputFiles;
29/**
30 * Try to load cbfiles (like gulp) from the users
31 * working directory
32 * @param config
33 * @returns {InputFiles}
34 */
35function retrieveCBFiles(config) {
36 var defaultCBFiles = ["cbfile.js", "crossbowfile.js"];
37 var maybes = (function () {
38 if (config.cbfile) {
39 return [config.cbfile];
40 }
41 return defaultCBFiles;
42 })();
43 return readInputFiles(maybes, config.cwd);
44}
45exports.retrieveCBFiles = retrieveCBFiles;
46/**
47 * Try to retrieve input files from disk.
48 * This is different from regular file reading as
49 * we deliver errors with context
50 */
51function readInputFiles(paths, cwd) {
52 /**
53 * Get files that exist on disk
54 * @type {ExternalFile[]}
55 */
56 var inputFiles = readFilesFromDisk(paths, cwd);
57 /**
58 * Add parsed input keys to them
59 * @type {}
60 */
61 var inputs = inputFiles.map(function (inputFile) {
62 /**
63 * If the file does not exist, change the error to be an InputFileNotFound error
64 * as this will allow more descriptive logging when needed
65 */
66 if (inputFile.errors.length) {
67 return _.assign({}, inputFile, {
68 // here there may be any types of file error,
69 // but we only care that was an error, and normalise it
70 // here for logging. We can added nice per-error messages later.
71 errors: [{ type: task_utils_1.InputErrorTypes.InputFileNotFound }],
72 input: undefined
73 });
74 }
75 /**
76 * If the input file was yaml, load it & translate to JS
77 */
78 if (inputFile.parsed.ext.match(/ya?ml$/i)) {
79 var yml = require("js-yaml");
80 try {
81 return _.assign(inputFile, {
82 input: yml.safeLoad(fs_1.readFileSync(inputFile.resolved, "utf8"))
83 });
84 }
85 catch (e) {
86 return _.assign(inputFile, {
87 input: undefined,
88 errors: [{ type: task_utils_1.InputErrorTypes.InvalidYaml, error: e }]
89 });
90 }
91 }
92 /**
93 * Finally assume a JS/JSON file and 'require' it as normal
94 */
95 try {
96 return _.assign({}, inputFile, {
97 input: require(inputFile.resolved)
98 });
99 }
100 catch (e) {
101 return _.assign(inputFile, {
102 input: undefined,
103 errors: [{ type: task_utils_1.InputErrorTypes.InvalidInput, error: e }]
104 });
105 }
106 });
107 return {
108 all: inputs,
109 valid: inputs.filter(function (x) { return x.errors.length === 0; }),
110 invalid: inputs.filter(function (x) { return x.errors.length > 0; })
111 };
112}
113exports.readInputFiles = readInputFiles;
114function readFileFromDiskWithContent(path, cwd) {
115 var files = readFilesFromDisk([path], cwd);
116 return files
117 .map(function (x) {
118 if (x.errors.length)
119 return x;
120 x.content = fs_1.readFileSync(x.resolved, "utf8");
121 return x;
122 })[0];
123}
124exports.readFileFromDiskWithContent = readFileFromDiskWithContent;
125function readFilesFromDiskWithContent(paths, cwd) {
126 var files = readFilesFromDisk(paths, cwd);
127 return files
128 .map(function (x) {
129 if (x.errors.length)
130 return x;
131 x.content = fs_1.readFileSync(x.resolved, "utf8");
132 return x;
133 });
134}
135exports.readFilesFromDiskWithContent = readFilesFromDiskWithContent;
136function readFileContent(file) {
137 return fs_1.readFileSync(file.resolved, "utf8");
138}
139exports.readFileContent = readFileContent;
140function writeFileToDisk(file, content) {
141 var mkdirp = require("mkdirp").sync;
142 mkdirp(path_3.dirname(file.resolved));
143 fs_1.writeFileSync(file.resolved, content);
144}
145exports.writeFileToDisk = writeFileToDisk;
146function getStubFileWithContent(path, cwd) {
147 var file = getStubFile(path, cwd);
148 file.content = "";
149 return file;
150}
151exports.getStubFileWithContent = getStubFileWithContent;
152function readOrCreateJsonFile(path, cwd) {
153 var existing = readFilesFromDiskWithContent([path], cwd)[0];
154 if (existing.errors.length) {
155 if (existing.errors[0].type === task_utils_1.InputErrorTypes.FileNotFound) {
156 var stub = getStubFileWithContent(path, cwd);
157 stub.content = "{}";
158 stub.data = JSON.parse(stub.content);
159 return stub;
160 }
161 }
162 else {
163 try {
164 existing.data = JSON.parse(existing.content);
165 }
166 catch (e) {
167 existing.data = {};
168 }
169 }
170 return existing;
171}
172exports.readOrCreateJsonFile = readOrCreateJsonFile;
173function getStubFile(path, cwd) {
174 var resolved = path_1.resolve(cwd, path);
175 return {
176 errors: [],
177 rawInput: path,
178 resolved: resolved,
179 parsed: path_1.parse(resolved),
180 relative: path_1.relative(cwd, resolved)
181 };
182}
183exports.getStubFile = getStubFile;
184/**
185 * Take an array of paths and return file info + errors if they don't exist
186 * @param paths
187 * @param cwd
188 * @returns {ExternalFile[]}
189 */
190function readFilesFromDisk(paths, cwd) {
191 return paths
192 .map(String)
193 .map(function (x) { return getStubFile(x, cwd); })
194 .map(function (incoming) {
195 var resolved = incoming.resolved;
196 /**
197 * If the path does not exist, it's a FileNotFound error
198 */
199 if (!fs_1.existsSync(resolved)) {
200 return _.assign(incoming, {
201 errors: [{ type: task_utils_1.InputErrorTypes.FileNotFound }]
202 });
203 }
204 /**
205 * Not check it's a file & NOT a dir
206 * @type {Stats}
207 */
208 var stat = fs_2.statSync(resolved);
209 if (!stat.isFile()) {
210 return _.assign(incoming, {
211 errors: [{ type: task_utils_1.InputErrorTypes.NotAFile }],
212 });
213 }
214 /**
215 * At this point the file DOES exist
216 */
217 return incoming;
218 });
219}
220exports.readFilesFromDisk = readFilesFromDisk;
221/**
222 * Attempt to use the LOCALLY installed crossbow version
223 * first, this will ensure anything registered with .task etc
224 * can be picked up by global installs too.
225 * @param config
226 * @returns {InputFiles}
227 */
228function getRequirePaths(config) {
229 var local = path_2.join("node_modules", "crossbow", "dist", "public", "create.js");
230 var global = path_2.join(__dirname, "public", "create.js");
231 return readInputFiles([local, global], config.cwd);
232}
233exports.getRequirePaths = getRequirePaths;
234function getExternalFiles(dirpaths, cwd) {
235 return dirpaths
236 .map(function (dirpath) {
237 return path_1.resolve(cwd, dirpath);
238 })
239 .filter(fs_1.existsSync)
240 .reduce(function (acc, dirPath) {
241 return acc.concat(fs_3.readdirSync(dirPath).map(function (filepath) {
242 var resolved = path_2.join(dirPath, filepath);
243 var parsed = path_1.parse(resolved);
244 var output = {
245 rawInput: filepath,
246 resolved: resolved,
247 relative: path_1.relative(cwd, resolved),
248 parsed: parsed,
249 errors: []
250 };
251 return output;
252 }));
253 }, []);
254}
255exports.getExternalFiles = getExternalFiles;
256function getPossibleTasksFromDirectories(dirpaths, cwd) {
257 return getExternalFiles(dirpaths, cwd)
258 .filter(function (x) { return supportedTaskFileExtensions.indexOf(x.parsed.ext) > -1; })
259 .map(function (x) {
260 return x.relative;
261 });
262}
263exports.getPossibleTasksFromDirectories = getPossibleTasksFromDirectories;
264var HashDirErrorTypes;
265(function (HashDirErrorTypes) {
266 HashDirErrorTypes[HashDirErrorTypes["HashNotADirectory"] = "HashNotADirectory"] = "HashNotADirectory";
267 HashDirErrorTypes[HashDirErrorTypes["HashPathNotFound"] = "HashPathNotFound"] = "HashPathNotFound";
268})(HashDirErrorTypes = exports.HashDirErrorTypes || (exports.HashDirErrorTypes = {}));
269function hashItems(dirs, cwd, existingHashes) {
270 return Rx.Observable
271 .from(dirs)
272 .map(function (x) {
273 return {
274 userInput: x,
275 pathObj: getStubFile(x, cwd)
276 };
277 })
278 .distinct(function (x) { return x.pathObj.resolved; })
279 .flatMap(hashFileOrDir)
280 .toArray()
281 .map(function (x) {
282 return markHashes(x, existingHashes);
283 });
284}
285exports.hashItems = hashItems;
286function hashFile(filepath, fn) {
287 var hash = crypto_1.createHash("sha256");
288 fs_4.createReadStream(filepath)
289 .on("data", function (chunk) {
290 hash.update(chunk);
291 })
292 .on("end", function () {
293 fn(null, hash.digest("hex"));
294 })
295 .on("error", fn);
296}
297function hashFileOrDir(input) {
298 return lstatAsObservable(input.pathObj.resolved).flatMap(function (stats) {
299 if (stats.isDirectory()) {
300 return hashDirAsObservable(input.pathObj.resolved).map(function (tree) {
301 return {
302 userInput: input.userInput,
303 resolved: input.pathObj.resolved,
304 hash: tree.hash
305 };
306 });
307 }
308 if (stats.isFile()) {
309 return hashFileAsObservable(input.pathObj.resolved).map(function (hash) {
310 return {
311 userInput: input.userInput,
312 resolved: input.pathObj.resolved,
313 hash: hash
314 };
315 });
316 }
317 return Rx.Observable.empty();
318 });
319}
320function markHashes(newHashes, existingHashes) {
321 var newHashPaths = newHashes.map(function (x) { return x.resolved; });
322 var markedHashes = newHashes.map(function (newHash) {
323 var match = existingHashes.filter(function (x) { return x.resolved === newHash.resolved; });
324 newHash.changed = (function () {
325 if (match.length) {
326 return match[0].hash !== newHash.hash;
327 }
328 return true; // return true by default so that new entries always run
329 })();
330 return newHash;
331 });
332 var otherHashes = existingHashes.filter(function (hash) {
333 return newHashPaths.indexOf(hash.resolved) === -1;
334 });
335 var output = otherHashes.concat(newHashes).filter(Boolean);
336 return {
337 output: output,
338 markedHashes: markedHashes
339 };
340}
341/**
342 * Thanks to https://github.com/motdotla/dotenv
343 * @param src
344 * @returns {{}}
345 */
346function parseEnv(src) {
347 var obj = {};
348 // convert Buffers before splitting into lines and processing
349 src.toString().split('\n').forEach(function (line) {
350 // matching "KEY' and 'VAL' in 'KEY=VAL'
351 var keyValueArr = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/);
352 // matched?
353 if (keyValueArr != null) {
354 var key = keyValueArr[1];
355 // default undefined or missing values to empty string
356 var value = keyValueArr[2] ? keyValueArr[2] : '';
357 // expand newlines in quoted values
358 var len = value ? value.length : 0;
359 if (len > 0 && value.charAt(0) === '"' && value.charAt(len - 1) === '"') {
360 value = value.replace(/\\n/gm, '\n');
361 }
362 // remove any surrounding quotes and extra spaces
363 value = value.replace(/(^['"]|['"]$)/g, '').trim();
364 obj[key] = value;
365 }
366 });
367 return obj;
368}
369exports.parseEnv = parseEnv;
370exports.Right = function (x) { return ({
371 chain: function (f) { return f(x); },
372 map: function (f) { return exports.Right(f(x)); },
373 fold: function (f, g) { return g(x); },
374 inspect: function () { return "Right(" + x + ")"; }
375}); };
376exports.Left = function (x) { return ({
377 chain: function (f) { return exports.Left(x); },
378 map: function (f) { return exports.Left(x); },
379 fold: function (f, g) { return f(x); },
380 inspect: function () { return "Left(" + x + ")"; }
381}); };
382exports.tryCatch = function (f) {
383 try {
384 return exports.Right(f());
385 }
386 catch (e) {
387 return exports.Left(e);
388 }
389};
390//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS51dGlscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9maWxlLnV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSwyQ0FBcUU7QUFDckUseUJBQTJEO0FBQzNELDZCQUE4QztBQUc5Qyx5QkFBNEI7QUFDNUIsNkJBQTBCO0FBQzFCLHlCQUErQjtBQUMvQix1QkFBMEI7QUFDMUIsNkJBQTZCO0FBQzdCLHlCQUFvQztBQUNwQyxpQ0FBa0M7QUFDbEMseUJBQWdDO0FBRWhDLElBQU0sRUFBRSxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztBQUMvQixJQUFNLG1CQUFtQixHQUFHLEVBQUUsQ0FBQyxVQUFVLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDL0QsSUFBTSxvQkFBb0IsR0FBRyxFQUFFLENBQUMsVUFBVSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBQ3RFLElBQU0saUJBQWlCLEdBQUcsRUFBRSxDQUFDLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFLLENBQUMsQ0FBQztBQUVoRSxJQUFNLENBQUMsR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FBQztBQUV0QywwQ0FBMEM7QUFDMUMsSUFBTSwyQkFBMkIsR0FBRyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQztBQXVCbkQ7OztHQUdHO0FBQ0gsbUNBQTBDLE1BQTZCO0lBQ25FLElBQU0sa0JBQWtCLEdBQUcsQ0FBQyxlQUFlLEVBQUUsYUFBYSxFQUFFLGNBQWMsRUFBRSxlQUFlLENBQUMsQ0FBQztJQUM3RixNQUFNLENBQUMsY0FBYyxDQUFDLGtCQUFrQixFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUMxRCxDQUFDO0FBSEQsOERBR0M7QUFFRDs7Ozs7R0FLRztBQUNILHlCQUFnQyxNQUE2QjtJQUN6RCxJQUFNLGNBQWMsR0FBRyxDQUFDLFdBQVcsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO0lB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
\No newline at end of file